mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
fix pack
This commit is contained in:
committed by
Aiden McClelland
parent
ffb9a72e18
commit
3f416dda1b
2
appmgr/Cargo.lock
generated
2
appmgr/Cargo.lock
generated
@@ -698,7 +698,7 @@ dependencies = [
|
||||
"patch-db",
|
||||
"pin-project",
|
||||
"prettytable-rs",
|
||||
"rand 0.8.4",
|
||||
"rand 0.7.3",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rpassword",
|
||||
|
||||
@@ -68,7 +68,7 @@ openssl = "0.10.30"
|
||||
patch-db = { version="*", path="../../patch-db/patch-db" }
|
||||
pin-project = "1.0.6"
|
||||
prettytable-rs = "0.8.0"
|
||||
rand = "0.8.3"
|
||||
rand = "0.7.3"
|
||||
regex = "1.4.2"
|
||||
reqwest = { version="0.11.2", features=["stream", "json"] }
|
||||
rpassword = "5.0.0"
|
||||
|
||||
@@ -109,7 +109,7 @@ impl Action {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, HasModel)]
|
||||
#[serde(rename = "kebab-case")]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ActionImplementation {
|
||||
Docker(DockerAction),
|
||||
|
||||
@@ -4,13 +4,35 @@ use embassy::Error;
|
||||
use rpc_toolkit::run_cli;
|
||||
|
||||
fn inner_main() -> Result<(), Error> {
|
||||
simple_logging::log_to_stderr(log::LevelFilter::Info);
|
||||
run_cli!(
|
||||
embassy::main_api,
|
||||
app => app.name("Embassy CLI")
|
||||
app => app
|
||||
.name("Embassy CLI")
|
||||
.arg(
|
||||
clap::Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("verbosity")
|
||||
.short("v")
|
||||
.multiple(true)
|
||||
.takes_value(false),
|
||||
)
|
||||
.arg(Arg::with_name("host").long("host").short("h").takes_value(true))
|
||||
.arg(Arg::with_name("port").long("port").short("p").takes_value(true)),
|
||||
matches => EitherContext::Cli(CliContext::init(matches)?),
|
||||
matches => {
|
||||
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
||||
0 => log::LevelFilter::Off,
|
||||
1 => log::LevelFilter::Error,
|
||||
2 => log::LevelFilter::Warn,
|
||||
3 => log::LevelFilter::Info,
|
||||
4 => log::LevelFilter::Debug,
|
||||
_ => log::LevelFilter::Trace,
|
||||
});
|
||||
EitherContext::Cli(CliContext::init(matches)?)
|
||||
},
|
||||
|code| if code < 0 { 1 } else { code }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,33 @@ use embassy::Error;
|
||||
use rpc_toolkit::run_cli;
|
||||
|
||||
fn inner_main() -> Result<(), Error> {
|
||||
simple_logging::log_to_stderr(log::LevelFilter::Info);
|
||||
run_cli!(
|
||||
embassy::portable_api,
|
||||
app => app.name("Embassy SDK"),
|
||||
matches => EitherContext::Cli(CliContext::init(matches)?),
|
||||
app => app
|
||||
.name("Embassy SDK")
|
||||
.arg(
|
||||
clap::Arg::with_name("config")
|
||||
.short("c")
|
||||
.long("config")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("verbosity")
|
||||
.short("v")
|
||||
.multiple(true)
|
||||
.takes_value(false),
|
||||
),
|
||||
matches => {
|
||||
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
||||
0 => log::LevelFilter::Off,
|
||||
1 => log::LevelFilter::Error,
|
||||
2 => log::LevelFilter::Warn,
|
||||
3 => log::LevelFilter::Info,
|
||||
4 => log::LevelFilter::Debug,
|
||||
_ => log::LevelFilter::Trace,
|
||||
});
|
||||
EitherContext::Cli(CliContext::init(matches)?)
|
||||
},
|
||||
|code| if code < 0 { 1 } else { code }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ impl CharSet {
|
||||
self.0.iter().any(|r| r.0.contains(c))
|
||||
}
|
||||
pub fn gen<R: Rng>(&self, rng: &mut R) -> char {
|
||||
let mut idx = rng.gen_range(0..self.1);
|
||||
let mut idx = rng.gen_range(0, self.1);
|
||||
for r in &self.0 {
|
||||
if idx < r.1 {
|
||||
return std::convert::TryFrom::try_from(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::net::IpAddr;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use clap::ArgMatches;
|
||||
use reqwest::Proxy;
|
||||
use rpc_toolkit::reqwest::{Client, Url};
|
||||
@@ -11,15 +13,17 @@ use rpc_toolkit::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::rpc::RpcContextConfig;
|
||||
use crate::ResultExt;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct CliContextConfig {
|
||||
#[serde(deserialize_with = "deserialize_host")]
|
||||
pub host: Option<Host>,
|
||||
pub port: Option<u16>,
|
||||
#[serde(deserialize_with = "crate::util::deserialize_from_str_opt")]
|
||||
pub proxy: Option<Url>,
|
||||
pub developer_key_path: Option<PathBuf>,
|
||||
#[serde(flatten)]
|
||||
pub server_config: RpcContextConfig,
|
||||
}
|
||||
@@ -29,13 +33,15 @@ pub struct CliContextSeed {
|
||||
pub host: Host,
|
||||
pub port: u16,
|
||||
pub client: Client,
|
||||
pub developer_key_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CliContext(Arc<CliContextSeed>);
|
||||
impl CliContext {
|
||||
/// BLOCKING
|
||||
pub fn init(matches: &ArgMatches) -> Result<Self, crate::Error> {
|
||||
let cfg_path = Path::new(crate::CONFIG_PATH);
|
||||
let cfg_path = Path::new(matches.value_of("config").unwrap_or(crate::CONFIG_PATH));
|
||||
let mut base = if cfg_path.exists() {
|
||||
serde_yaml::from_reader(
|
||||
File::open(cfg_path)
|
||||
@@ -76,8 +82,29 @@ impl CliContext {
|
||||
} else {
|
||||
Client::new()
|
||||
},
|
||||
developer_key_path: base.developer_key_path.unwrap_or_else(|| {
|
||||
cfg_path
|
||||
.parent()
|
||||
.unwrap_or(Path::new("/"))
|
||||
.join(".developer_key")
|
||||
}),
|
||||
})))
|
||||
}
|
||||
/// BLOCKING
|
||||
pub fn developer_key(&self) -> Result<ed25519_dalek::Keypair, Error> {
|
||||
if !self.developer_key_path.exists() {
|
||||
return Err(Error::new(anyhow!("Developer Key does not exist! Please run `embassy-sdk init` before running this command."), crate::ErrorKind::Uninitialized));
|
||||
}
|
||||
let mut keypair_buf = [0; ed25519_dalek::KEYPAIR_LENGTH];
|
||||
File::open(&self.developer_key_path)?.read_exact(&mut keypair_buf)?;
|
||||
Ok(ed25519_dalek::Keypair::from_bytes(&keypair_buf)?)
|
||||
}
|
||||
}
|
||||
impl std::ops::Deref for CliContext {
|
||||
type Target = CliContextSeed;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
impl Context for CliContext {
|
||||
fn host(&self) -> Host<&str> {
|
||||
|
||||
@@ -80,7 +80,7 @@ impl std::fmt::Display for DependencyError {
|
||||
if !comma {
|
||||
comma = true;
|
||||
} else {
|
||||
write!(f, ", ");
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{} @ {} {}", check, res.time, res.result)?;
|
||||
}
|
||||
|
||||
29
appmgr/src/developer/mod.rs
Normal file
29
appmgr/src/developer/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use ed25519_dalek::Keypair;
|
||||
use rpc_toolkit::command;
|
||||
|
||||
use crate::context::EitherContext;
|
||||
use crate::util::display_none;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[command(cli_only, blocking, display(display_none))]
|
||||
pub fn init(#[context] ctx: EitherContext) -> Result<(), Error> {
|
||||
let ctx = ctx.as_cli().unwrap();
|
||||
if !ctx.developer_key_path.exists() {
|
||||
let parent = ctx.developer_key_path.parent().unwrap_or(Path::new("/"));
|
||||
if !parent.exists() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, parent.display().to_string()))?;
|
||||
}
|
||||
log::info!("Generating new developer key...");
|
||||
let keypair = Keypair::generate(&mut rand::thread_rng());
|
||||
log::info!("Writing key to {}", ctx.developer_key_path.display());
|
||||
let mut dev_key_file = File::create(&ctx.developer_key_path)?;
|
||||
dev_key_file.write_all(&keypair.to_bytes())?;
|
||||
dev_key_file.sync_all()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -45,6 +45,7 @@ pub enum ErrorKind {
|
||||
RateLimited = 37,
|
||||
InvalidRequest = 38,
|
||||
MigrationFailed = 39,
|
||||
Uninitialized = 40,
|
||||
}
|
||||
impl ErrorKind {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
@@ -89,6 +90,7 @@ impl ErrorKind {
|
||||
RateLimited => "Rate Limited",
|
||||
InvalidRequest => "Invalid Request",
|
||||
MigrationFailed => "Migration Failed",
|
||||
Uninitialized => "Uninitialized",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ pub mod config;
|
||||
pub mod context;
|
||||
pub mod db;
|
||||
pub mod dependencies;
|
||||
pub mod developer;
|
||||
pub mod error;
|
||||
pub mod id;
|
||||
pub mod install;
|
||||
@@ -46,12 +47,19 @@ pub fn echo(#[context] _ctx: EitherContext, #[arg] message: String) -> Result<St
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
#[command(subcommands(config::config, version::git_info, echo, s9pk::pack, s9pk::verify))]
|
||||
#[command(subcommands(
|
||||
config::config,
|
||||
version::git_info,
|
||||
echo,
|
||||
s9pk::pack,
|
||||
s9pk::verify,
|
||||
developer::init
|
||||
))]
|
||||
pub fn main_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
#[command(subcommands(version::git_info, s9pk::pack, s9pk::verify))]
|
||||
#[command(subcommands(version::git_info, s9pk::pack, s9pk::verify, developer::init))]
|
||||
pub fn portable_api(#[context] ctx: EitherContext) -> Result<EitherContext, RpcError> {
|
||||
Ok(ctx)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use torut::onion::TorSecretKeyV3;
|
||||
use crate::db::model::{InterfaceAddressMap, InterfaceAddresses, InterfaceInfo};
|
||||
use crate::id::Id;
|
||||
use crate::s9pk::manifest::PackageId;
|
||||
use crate::util::Port;
|
||||
use crate::Error;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@@ -99,7 +100,7 @@ impl<S: AsRef<str>> AsRef<Path> for InterfaceId<S> {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Interface {
|
||||
pub tor_config: Option<TorConfig>,
|
||||
pub lan_config: Option<IndexMap<u16, LanPortConfig>>,
|
||||
pub lan_config: Option<IndexMap<Port, LanPortConfig>>,
|
||||
pub ui: bool,
|
||||
pub protocols: Vec<String>,
|
||||
}
|
||||
@@ -107,7 +108,7 @@ pub struct Interface {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct TorConfig {
|
||||
pub port_mapping: IndexMap<u16, u16>,
|
||||
pub port_mapping: IndexMap<Port, Port>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
||||
@@ -147,7 +147,7 @@ impl TorControllerInner {
|
||||
.port_mapping
|
||||
.iter()
|
||||
.map(|(external, internal)| {
|
||||
(*external, SocketAddr::from((config.ip, *internal)))
|
||||
(external.0, SocketAddr::from((config.ip, internal.0)))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.iter(),
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
|
||||
use digest::Digest;
|
||||
use sha2::Sha512;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use super::header::{FileSection, Header};
|
||||
use super::manifest::Manifest;
|
||||
use super::SIG_CONTEXT;
|
||||
use crate::util::HashWriter;
|
||||
use crate::{Error, ResultExt};
|
||||
|
||||
#[derive(TypedBuilder)]
|
||||
@@ -32,7 +36,7 @@ impl<
|
||||
> S9pkPacker<'a, W, RLicense, RInstructions, RIcon, RDockerImages>
|
||||
{
|
||||
/// BLOCKING
|
||||
pub fn pack(mut self) -> Result<(), Error> {
|
||||
pub fn pack(mut self, key: &ed25519_dalek::Keypair) -> Result<(), Error> {
|
||||
let header_pos = self.writer.stream_position()?;
|
||||
if header_pos != 0 {
|
||||
log::warn!("Appending to non-empty file.");
|
||||
@@ -45,57 +49,63 @@ impl<
|
||||
)
|
||||
})?;
|
||||
let mut position = self.writer.stream_position()?;
|
||||
|
||||
let mut writer = HashWriter::new(Sha512::new(), &mut self.writer);
|
||||
// manifest
|
||||
serde_cbor::to_writer(&mut self.writer, self.manifest).with_ctx(|_| {
|
||||
serde_cbor::to_writer(&mut writer, self.manifest).with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Serialization,
|
||||
"Serializing Manifest (CBOR)",
|
||||
)
|
||||
})?;
|
||||
let new_pos = self.writer.stream_position()?;
|
||||
let new_pos = writer.stream_position()?;
|
||||
header.table_of_contents.manifest = FileSection {
|
||||
position,
|
||||
length: new_pos - position,
|
||||
};
|
||||
position = new_pos;
|
||||
// license
|
||||
std::io::copy(&mut self.license, &mut self.writer)
|
||||
std::io::copy(&mut self.license, &mut writer)
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying License"))?;
|
||||
let new_pos = self.writer.stream_position()?;
|
||||
let new_pos = writer.stream_position()?;
|
||||
header.table_of_contents.license = FileSection {
|
||||
position,
|
||||
length: new_pos - position,
|
||||
};
|
||||
position = new_pos;
|
||||
// instructions
|
||||
std::io::copy(&mut self.instructions, &mut self.writer)
|
||||
std::io::copy(&mut self.instructions, &mut writer)
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Instructions"))?;
|
||||
let new_pos = self.writer.stream_position()?;
|
||||
let new_pos = writer.stream_position()?;
|
||||
header.table_of_contents.instructions = FileSection {
|
||||
position,
|
||||
length: new_pos - position,
|
||||
};
|
||||
position = new_pos;
|
||||
// icon
|
||||
std::io::copy(&mut self.icon, &mut self.writer)
|
||||
std::io::copy(&mut self.icon, &mut writer)
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying Icon"))?;
|
||||
let new_pos = self.writer.stream_position()?;
|
||||
let new_pos = writer.stream_position()?;
|
||||
header.table_of_contents.icon = FileSection {
|
||||
position,
|
||||
length: new_pos - position,
|
||||
};
|
||||
position = new_pos;
|
||||
// docker_images
|
||||
std::io::copy(&mut self.docker_images, &mut self.writer)
|
||||
std::io::copy(&mut self.docker_images, &mut writer)
|
||||
.with_ctx(|_| (crate::ErrorKind::Filesystem, "Copying App Image"))?;
|
||||
let new_pos = self.writer.stream_position()?;
|
||||
let new_pos = writer.stream_position()?;
|
||||
header.table_of_contents.docker_images = FileSection {
|
||||
position,
|
||||
length: new_pos - position,
|
||||
};
|
||||
position = new_pos;
|
||||
|
||||
// header
|
||||
let (hash, _) = writer.finish();
|
||||
self.writer.seek(SeekFrom::Start(header_pos))?;
|
||||
header.pubkey = key.public.clone();
|
||||
header.signature = key.sign_prehashed(hash, Some(SIG_CONTEXT))?;
|
||||
header
|
||||
.serialize(&mut self.writer)
|
||||
.with_ctx(|_| (crate::ErrorKind::Serialization, "Writing Header"))?;
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::Error;
|
||||
pub const MAGIC: [u8; 2] = [59, 59];
|
||||
pub const VERSION: u8 = 1;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Header {
|
||||
pub pubkey: PublicKey,
|
||||
pub signature: Signature,
|
||||
@@ -76,14 +77,11 @@ pub struct TableOfContents {
|
||||
}
|
||||
impl TableOfContents {
|
||||
pub fn serialize<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
|
||||
let len: u32 = 16 // size of FileSection
|
||||
* (
|
||||
1 + // manifest
|
||||
1 + // license
|
||||
1 + // instructions
|
||||
1 + // icon
|
||||
1 // docker_images
|
||||
);
|
||||
let len: u32 = ((1 + "manifest".len() + 16)
|
||||
+ (1 + "license".len() + 16)
|
||||
+ (1 + "instructions".len() + 16)
|
||||
+ (1 + "icon".len() + 16)
|
||||
+ (1 + "docker_images".len() + 16)) as u32;
|
||||
writer.write_all(&u32::to_be_bytes(len))?;
|
||||
self.manifest.serialize_entry("manifest", &mut writer)?;
|
||||
self.license.serialize_entry("license", &mut writer)?;
|
||||
@@ -153,7 +151,8 @@ impl FileSection {
|
||||
if read == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
let label = vec![0; label_len[0] as usize];
|
||||
let mut label = vec![0; label_len[0] as usize];
|
||||
reader.read_exact(&mut label).await?;
|
||||
let mut pos = [0; 8];
|
||||
reader.read_exact(&mut pos).await?;
|
||||
let mut len = [0; 8];
|
||||
|
||||
@@ -134,6 +134,7 @@ pub struct Manifest {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Assets {
|
||||
#[serde(default)]
|
||||
pub license: Option<PathBuf>,
|
||||
@@ -168,7 +169,7 @@ impl Assets {
|
||||
self.docker_images
|
||||
.as_ref()
|
||||
.map(|a| a.as_path())
|
||||
.unwrap_or(Path::new("images.tar"))
|
||||
.unwrap_or(Path::new("image.tar"))
|
||||
}
|
||||
pub fn instructions_path(&self) -> &Path {
|
||||
self.instructions
|
||||
|
||||
@@ -20,8 +20,11 @@ pub mod reader;
|
||||
pub const SIG_CONTEXT: &'static [u8] = b"s9pk";
|
||||
|
||||
#[command(cli_only, display(display_none), blocking)]
|
||||
pub fn pack(#[context] _ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
||||
pub fn pack(#[context] ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Result<(), Error> {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
let ctx = ctx.as_cli().unwrap();
|
||||
|
||||
let path = if let Some(path) = path {
|
||||
path
|
||||
@@ -44,12 +47,40 @@ pub fn pack(#[context] _ctx: EitherContext, #[arg] path: Option<PathBuf>) -> Res
|
||||
S9pkPacker::builder()
|
||||
.manifest(&manifest)
|
||||
.writer(&mut outfile)
|
||||
.license(File::open(path.join(manifest.assets.license_path()))?)
|
||||
.icon(File::open(path.join(manifest.assets.icon_path()))?)
|
||||
.instructions(File::open(path.join(manifest.assets.instructions_path()))?)
|
||||
.docker_images(File::open(path.join(manifest.assets.docker_images_path()))?)
|
||||
.license(
|
||||
File::open(path.join(manifest.assets.license_path())).with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
manifest.assets.license_path().display().to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.icon(
|
||||
File::open(path.join(manifest.assets.icon_path())).with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
manifest.assets.icon_path().display().to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.instructions(
|
||||
File::open(path.join(manifest.assets.instructions_path())).with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
manifest.assets.instructions_path().display().to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.docker_images(
|
||||
File::open(path.join(manifest.assets.docker_images_path())).with_ctx(|_| {
|
||||
(
|
||||
crate::ErrorKind::Filesystem,
|
||||
manifest.assets.docker_images_path().display().to_string(),
|
||||
)
|
||||
})?,
|
||||
)
|
||||
.build()
|
||||
.pack()?;
|
||||
.pack(&ctx.developer_key()?)?;
|
||||
outfile.sync_all()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -67,7 +67,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<InstallProgressTracker<R>> {
|
||||
}
|
||||
impl<R: AsyncRead + AsyncSeek + Unpin> S9pkReader<R> {
|
||||
pub async fn validate(&mut self) -> Result<(), Error> {
|
||||
todo!()
|
||||
Ok(())
|
||||
}
|
||||
pub async fn from_reader(mut rdr: R) -> Result<Self, Error> {
|
||||
let header = Header::deserialize(&mut rdr).await?;
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::time::Duration;
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use clap::ArgMatches;
|
||||
use digest::Digest;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
@@ -620,7 +621,7 @@ impl<W: std::fmt::Write> std::io::Write for FmtWriter<W> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename = "kebab-case")]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum IoFormat {
|
||||
Json,
|
||||
JsonPretty,
|
||||
@@ -698,6 +699,7 @@ impl IoFormat {
|
||||
.with_kind(crate::ErrorKind::Serialization),
|
||||
}
|
||||
}
|
||||
/// BLOCKING
|
||||
pub fn from_reader<R: std::io::Read, T: for<'de> Deserialize<'de>>(
|
||||
&self,
|
||||
mut reader: R,
|
||||
@@ -714,7 +716,9 @@ impl IoFormat {
|
||||
}
|
||||
IoFormat::Toml | IoFormat::TomlPretty => {
|
||||
let mut s = String::new();
|
||||
reader.read_to_string(&mut s);
|
||||
reader
|
||||
.read_to_string(&mut s)
|
||||
.with_kind(crate::ErrorKind::Deserialization)?;
|
||||
serde_toml::from_str(&s).with_kind(crate::ErrorKind::Deserialization)
|
||||
}
|
||||
}
|
||||
@@ -809,3 +813,56 @@ impl<T> Container<T> {
|
||||
*self.0.write().await = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HashWriter<H: Digest, W: std::io::Write> {
|
||||
hasher: H,
|
||||
writer: W,
|
||||
}
|
||||
impl<H: Digest, W: std::io::Write> HashWriter<H, W> {
|
||||
pub fn new(hasher: H, writer: W) -> Self {
|
||||
HashWriter { hasher, writer }
|
||||
}
|
||||
pub fn finish(self) -> (H, W) {
|
||||
(self.hasher, self.writer)
|
||||
}
|
||||
}
|
||||
impl<H: Digest, W: std::io::Write> std::io::Write for HashWriter<H, W> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let written = self.writer.write(buf)?;
|
||||
self.hasher.update(&buf[..written]);
|
||||
Ok(written)
|
||||
}
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.writer.flush()
|
||||
}
|
||||
}
|
||||
impl<H: Digest, W: std::io::Write> std::ops::Deref for HashWriter<H, W> {
|
||||
type Target = W;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.writer
|
||||
}
|
||||
}
|
||||
impl<H: Digest, W: std::io::Write> std::ops::DerefMut for HashWriter<H, W> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.writer
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Port(pub u16);
|
||||
impl<'de> Deserialize<'de> for Port {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserialize_from_str(deserializer).map(Port)
|
||||
}
|
||||
}
|
||||
impl Serialize for Port {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serialize_display(&self.0, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user