Compare commits

...

2 commits

Author SHA1 Message Date
fa6591ab87 Add actor_id to NodeConfig 2025-05-24 09:17:45 +09:00
ba87ccf734 Add example, config, cli 2025-05-24 06:11:00 +09:00
13 changed files with 240 additions and 2 deletions

View file

@ -1,5 +1,5 @@
[workspace]
members = [ "lazy-supplements", "lazy-supplements-*" ]
members = [ "examples/timestamper", "lazy-supplements", "lazy-supplements-*" ]
[workspace.package]
edition = "2024"
@ -9,4 +9,5 @@ license = "MIT OR Apache-2.0"
repository = "https://forgejo.fireturlte.net"
[workspace.dependencies]
lazy-supplements.path = "lazy-supplements"
libp2p = "0.55.0"

View file

@ -0,0 +1,10 @@
[package]
name = "timestamper"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
lazy-supplements.workspace = true

View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View file

@ -8,4 +8,11 @@ repository.workspace = true
[dependencies]
automerge = "0.6.1"
libp2p.workspace = true
autosurgeon = "0.8.7"
base64 = "0.22.1"
clap = { version = "4.5.38", features = ["derive"] }
libp2p.workspace = true
serde = { version = "1.0.219", features = ["derive"] }
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["macros", "rt"] }
toml = "0.8.22"

View file

@ -0,0 +1,13 @@
use std::{net::IpAddr, path::PathBuf};
use clap::Args;
#[derive(Args, Debug)]
pub struct ConnectArgs {
#[arg(long)]
endpoint: IpAddr,
#[arg(long)]
port: i32,
#[arg(long)]
config: PathBuf,
}

View file

@ -0,0 +1,29 @@
use std::path::PathBuf;
use clap::Args;
use libp2p::identity;
#[derive(Args, Debug)]
pub struct InitArgs {
#[arg(long)]
config: Option<PathBuf>
}
impl InitArgs {
fn main(self) {
let config_path = if let Some(x) = self.config {
x
} else {
crate::cli::default_config_path()
};
if config_path.exists() {
println!("Config file already exists!");
return;
} else {
let keypair = identity::Keypair::generate_ed25519();
let buf = keypair.to_protobuf_encoding().unwrap();
}
}
}

View file

@ -0,0 +1,9 @@
use std::path::PathBuf;
mod connect;
mod init;
mod server;
pub fn default_config_path() -> PathBuf {
todo!()
}

View file

@ -0,0 +1,13 @@
use std::{net::IpAddr, path::PathBuf};
use clap::Args;
use crate::config::PartialServerConfig;
#[derive(Args, Debug)]
pub struct ServerArgs {
#[command(flatten)]
server_config: PartialServerConfig,
#[arg(long)]
config: PathBuf,
}

View file

@ -0,0 +1,13 @@
mod node;
mod server;
pub use node::NodeConfig;
pub use server::{
PartialServerConfig,
ServerConfig,
DEFAULT_LISTEN_IPS,
DEFAULT_PORT,
DEFAULT_PARTIAL_SERVER_CONFIG,
DEFAULT_SERVER_CONFIG
};

View file

@ -0,0 +1,77 @@
use automerge::ActorId;
use libp2p::identity::{self, Keypair};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
pub struct NodeConfig {
#[serde(with = "keypair")]
secret: Keypair,
#[serde(with = "actor_id")]
actor_id: ActorId
}
mod keypair {
use base64::{prelude::BASE64_STANDARD, Engine};
use libp2p::identity::Keypair;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(keypair: &Keypair, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let vec = keypair.to_protobuf_encoding().unwrap();
let base64 = BASE64_STANDARD.encode(vec);
serializer.serialize_str(&base64)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Keypair, D::Error>
where D: Deserializer<'de>
{
let base64 = String::deserialize(deserializer)?;
let vec = BASE64_STANDARD.decode(base64).unwrap();
Ok(Keypair::from_protobuf_encoding(&vec).unwrap())
}
}
mod actor_id {
use automerge::ActorId;
use base64::{prelude::BASE64_STANDARD, Engine};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(actor_id: &ActorId, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
let bytes = actor_id.to_bytes();
let base64 = BASE64_STANDARD.encode(bytes);
serializer.serialize_str(&base64)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<ActorId, D::Error>
where D: Deserializer<'de>
{
let base64 = String::deserialize(deserializer)?;
let vec = BASE64_STANDARD.decode(base64).unwrap();
Ok(ActorId::from(vec))
}
}
#[cfg(test)]
mod tests {
use automerge::ActorId;
use libp2p::identity;
use super::*;
#[tokio::test]
async fn serialize_deserialize() {
let keypair = identity::Keypair::generate_ed25519();
let actor_id = ActorId::random();
let config = NodeConfig {
secret: keypair.clone(),
actor_id: actor_id.clone(),
};
let string = toml::to_string(&config).unwrap();
println!("Parsed config: {}", &string);
let parsed_config: NodeConfig = toml::from_str(&string).unwrap();
assert_eq!(keypair.public(), parsed_config.secret.public());
assert_eq!(actor_id, parsed_config.actor_id);
}
}

View file

@ -0,0 +1,55 @@
use std::{collections::HashSet, net::{IpAddr, Ipv4Addr}, str::FromStr, sync::LazyLock};
use automerge::hydrate::List;
use clap::Args;
use serde::{Deserialize, Serialize};
use crate::error::Error;
pub static DEFAULT_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))];
pub static DEFAULT_PORT: u16 = 8080;
pub static DEFAULT_SERVER_CONFIG: LazyLock<ServerConfig> = LazyLock::new(|| {
ServerConfig{
listen_ips: Vec::from(DEFAULT_LISTEN_IPS),
port: DEFAULT_PORT
}
});
pub static DEFAULT_PARTIAL_SERVER_CONFIG: LazyLock<PartialServerConfig> = LazyLock::new(|| {
PartialServerConfig::from((*DEFAULT_SERVER_CONFIG).clone())
});
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ServerConfig {
listen_ips: Vec<IpAddr>,
port: u16,
}
impl TryFrom<PartialServerConfig> for ServerConfig {
type Error = Error;
fn try_from(config: PartialServerConfig) -> Result<ServerConfig, Self::Error>{
Ok(ServerConfig {
listen_ips: config.listen_ips.ok_or(Error::MissingConfig("listen_ips".to_string()))?,
port: config.port.ok_or(Error::MissingConfig("port".to_string()))?
})
}
}
#[derive(Args, Debug, Deserialize, Serialize)]
pub struct PartialServerConfig {
#[arg(long)]
listen_ips: Option<Vec<IpAddr>>,
#[arg(long)]
port: Option<u16>,
}
impl From<ServerConfig> for PartialServerConfig {
fn from(config: ServerConfig) -> PartialServerConfig {
PartialServerConfig {
listen_ips: Some(config.listen_ips),
port: Some(config.port)
}
}
}

View file

@ -0,0 +1,5 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("mandatory config `{0}` is missing")]
MissingConfig(String),
}

View file

@ -0,0 +1,3 @@
pub mod cli;
pub mod config;
pub mod error;