connect state

This commit is contained in:
Masato Imai
2025-02-24 12:41:35 +00:00
parent 433ded8f47
commit cd5d627244
9 changed files with 200 additions and 3 deletions

14
src/bgp_type.rs Normal file
View File

@ -0,0 +1,14 @@
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord)]
pub struct AutonomousSystemNumber(u16);
impl From<AutonomousSystemNumber> for u16 {
fn from(as_number: AutonomousSystemNumber) -> u16 {
as_number.0
}
}
impl From<u16> for AutonomousSystemNumber {
fn from(as_number: u16) -> Self {
Self(as_number)
}
}

76
src/config.rs Normal file
View File

@ -0,0 +1,76 @@
use std::{
net::{IpAddr, Ipv4Addr},
str::FromStr,
};
use anyhow::Context;
use crate::{bgp_type::AutonomousSystemNumber, error::ConfigParseError};
#[derive(PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
pub struct Config {
pub local_as: AutonomousSystemNumber,
pub local_ip: IpAddr,
pub remote_as: AutonomousSystemNumber,
pub remote_ip: IpAddr,
pub mode: Mode,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord)]
pub enum Mode {
Passive,
Active,
}
impl FromStr for Mode {
type Err = ConfigParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"passive" | "Passive" => Ok(Mode::Passive),
"active" | "Active" => Ok(Mode::Active),
_ => Err(ConfigParseError::from(anyhow::anyhow!("cannot parse {s}"))),
}
}
}
impl FromStr for Config {
type Err = ConfigParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let config: Vec<&str> = s.split(' ').collect();
let local_as = AutonomousSystemNumber::from(config[0].parse::<u16>().context(format!(
"cannot parse 1st part of config, '{0}', as as-number and config is {1}",
config[0], s
))?);
let local_ip: Ipv4Addr = config[1].parse().context(format!(
"cannot parse 2nd part of config, '{0}', as ip-address and config is {1}",
config[1], s
))?;
let remote_as = AutonomousSystemNumber::from(config[2].parse::<u16>().context(format!(
"cannot parse 3rd part of config, '{0}', as as-number and config is {1}",
config[2], s
))?);
let remote_ip: Ipv4Addr = config[3].parse().context(format!(
"cannot parse 4th part of config, '{0}', as ip-address and config is {1}",
config[3], s
))?;
let mode = Mode::from_str(config[4]).context(format!(
"cannot parse 5th part of config, '{0}', as mode and config is {1}",
config[4], s
))?;
Ok(Config {
local_as,
local_ip: IpAddr::V4(local_ip),
remote_as,
remote_ip: IpAddr::V4(remote_ip),
mode,
})
}
}

8
src/error.rs Normal file
View File

@ -0,0 +1,8 @@
use thiserror::Error;
#[derive(Error, Debug)]
#[error(transparent)]
pub struct ConfigParseError {
#[from]
source: anyhow::Error,
}

4
src/event.rs Normal file
View File

@ -0,0 +1,4 @@
#[derive(PartialEq, Eq, Debug, Clone, Hash)]
pub enum Event {
ManualStart,
}

20
src/event_queue.rs Normal file
View File

@ -0,0 +1,20 @@
use std::collections::VecDeque;
use crate::event::Event;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct EventQueue(VecDeque<Event>);
impl EventQueue {
pub fn new() -> Self {
Self(VecDeque::new())
}
pub fn enqueue(&mut self, event: Event) {
self.0.push_front(event);
}
pub fn dequeue(&mut self) -> Option<Event> {
self.0.pop_back()
}
}

9
src/lib.rs Normal file
View File

@ -0,0 +1,9 @@
#![allow(dead_code, unused)]
mod bgp_type;
pub mod config;
mod error;
mod event;
mod event_queue;
pub mod peer;
mod state;

View File

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

63
src/peer.rs Normal file
View File

@ -0,0 +1,63 @@
use tracing::info;
use crate::{config::Config, event::Event, event_queue::EventQueue, state::State};
#[derive(Debug)]
pub struct Peer {
state: State,
event_queue: EventQueue,
config: Config,
}
impl Peer {
pub fn new(config: Config) -> Self {
let state = State::Idle;
let event_queue = EventQueue::new();
Self {
state,
event_queue,
config,
}
}
pub fn start(&mut self) {
info!("peer started");
self.event_queue.enqueue(Event::ManualStart);
}
pub async fn next(&mut self) {
if let Some(event) = self.event_queue.dequeue() {
info!("event occurred, event={:?}", event);
self.handle_event(event).await;
}
}
async fn handle_event(&mut self, event: Event) {
match &self.state {
State::Idle => match event {
Event::ManualStart => {
self.state = State::Connect;
}
_ => {}
},
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::peer::Peer;
use crate::state::State;
#[tokio::test]
async fn peer_can_transition_to_connect_state() {
let config: Config = "64512 127.0.0.1 65413 127.0.0.2 active".parse().unwrap();
let mut peer = Peer::new(config);
peer.start();
peer.next().await;
assert_eq!(peer.state, State::Connect);
}
}

5
src/state.rs Normal file
View File

@ -0,0 +1,5 @@
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub enum State {
Idle,
Connect,
}