Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mxdamien committed Nov 16, 2022
0 parents commit 83c57a7
Show file tree
Hide file tree
Showing 13 changed files with 471 additions and 0 deletions.
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

env:
CARGO_TERM_COLOR: always

jobs:
LinuxLatest:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v1

- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

WindowsLatest:

runs-on: windows-latest

steps:
- uses: actions/checkout@v2

- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v1

- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

MacOsLatest:

runs-on: macos-latest

steps:
- uses: actions/checkout@v2

- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: Swatinem/rust-cache@v1

- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
/.vscode
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "dbtimetable"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
confy = { version = "0.3.1" }
serde = { version = "1.0", features = ["derive"] }
quick-xml = { version = "0.23.0", features = ["serialize"] }
chrono = { version = "0.4.19" }
merge = { version = "0.1.0" }
21 changes: 21 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 David Langhals

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## dbtimetable
A small Rust application to query the Deutsche Bahn (DB) timetable API. It can be used to display changes for a set of train stations

## Setup
You need to register with the DB API Marketplace to use the timeable API.
[Link to API description](https://developers.deutschebahn.com/db-api-marketplace/apis/product/timetables/api/26494#/Timetables_10213/overview)
You also need to obtain a Client ID and an API Key for the timeable API and set them in your local configuration. [config.rs](src/config.rs)

## Configure your stations
Stations are identified by their EVA numbers. The stations you want to observe have to be configured in your local configuration [config.rs](src/config.rs)
A list of stations alongside their EVA numbers can be found here: [Link](https://data.deutschebahn.com/dataset/data-haltestellen.html)

## License

[License](LICENSE.md)
51 changes: 51 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::{
config::Config, dbapiclient::DbApiClient, timetablepresenter::TimetablePresenter,
xmlparser::XmlParser,
};

pub struct App {
apiclient: DbApiClient,
xmlparser: XmlParser,
config: Config,
presenter: Box<dyn TimetablePresenter>,
}

impl App {
fn construct_changes_endpoint(eva: &String) -> String {
format!("fchg/{}", eva)
}

pub fn new(
apiclient: DbApiClient,
xmlparser: XmlParser,
config: Config,
presenter: Box<dyn TimetablePresenter>,
) -> Self {
Self {
apiclient,
xmlparser,
config,
presenter,
}
}

pub async fn run(self) {
for eva in &self.config.evas {
let timetable_changes = match self
.apiclient
.get(App::construct_changes_endpoint(eva))
.await
{
Ok(s) => s,
Err(_) => "".into(),
};

match self.xmlparser.get_timetable(&timetable_changes) {
Ok(changes) => {
self.presenter.present(&changes);
}
Err(_) => (),
}
}
}
}
20 changes: 20 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Clone)]
pub struct Config {
pub url: String,
pub client_id: String,
pub api_key: String,
pub evas: Vec<String>
}

impl ::std::default::Default for Config {
fn default() -> Self {
Self {
url: "https://apis.deutschebahn.com/db-api-marketplace/apis/timetables/v1/".to_string(),
client_id: "123456789".to_string(),
api_key: "123456789".to_string(),
evas: vec!("8003368".to_string())
}
}
}
46 changes: 46 additions & 0 deletions src/dbapiclient.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::config::Config;
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT};

pub struct DbApiClient {
config: Config,
}

impl DbApiClient {
pub fn new(config: Config) -> Self {
Self { config }
}

fn construct_headers(&self) -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(
"DB-Api-Key",
HeaderValue::from_str(self.config.api_key.as_str()).unwrap(),
);
headers.insert(
"DB-Client-Id",
HeaderValue::from_str(self.config.client_id.as_str()).unwrap(),
);
headers.insert(ACCEPT, HeaderValue::from_static("application/xml"));
headers
}

fn construct_complete_url(&self, endpoint: String) -> String {
format!("{}{}", self.config.url, endpoint)
}

pub async fn get(&self, endpoint: String) -> Result<String, reqwest::Error> {
let client = reqwest::Client::new();
let res = client
.get(self.construct_complete_url(endpoint))
.headers(self.construct_headers())
.send()
.await?;

let body = match res.text().await {
Ok(it) => it,
Err(_err) => return Err(_err),
};

Ok(body)
}
}
27 changes: 27 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod app;
mod config;
mod dbapiclient;
mod timetable;
mod timetablepresenter;
mod timetablepresenterconsole;
mod xmlparser;

use app::App;
use config::Config;
use dbapiclient::DbApiClient;
use timetablepresenterconsole::TimetablePresenterConsole;
use xmlparser::XmlParser;

extern crate confy;

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let config: Config = confy::load("dbtimetable")?;
let apiclient = DbApiClient::new(config.clone());
let xmlparser = XmlParser::new();
let presenter = Box::new(TimetablePresenterConsole::new());
let app = App::new(apiclient, xmlparser, config.clone(), presenter);
app.run().await;

Ok(())
}
79 changes: 79 additions & 0 deletions src/timetable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use serde::Deserialize;
use merge::Merge;

#[derive(Deserialize, Merge)]
pub struct ArrivalDeparture {
pub cde: Option<String>,
pub clt: Option<String>,
pub cp: Option<String>,
pub cpth: Option<String>,
pub cs: Option<String>,
pub ct: Option<String>,
pub dc: Option<i64>,
pub hi: Option<i32>,
pub l: Option<String>,
pub m: Option<Vec<Message>>,
pub pde: Option<String>,
pub pp: Option<String>,
pub ppth: Option<String>,
pub ps: Option<String>,
pub pt: Option<String>,
pub tra: Option<String>,
pub wings: Option<String>,
}

#[derive(Deserialize, Merge)]
pub struct TimetableStop {
pub eva: Option<i64>,
pub id: Option<String>,
pub ar: Option<ArrivalDeparture>,
pub conn: Option<String>,
pub dp: Option<ArrivalDeparture>,
pub hd: Option<String>,
pub hpc: Option<String>,
pub m: Option<Vec<Message>>,
pub r: Option<String>,
pub rtr: Option<String>,
pub tl: Option<Triplabel>,
pub f: Option<String>,
}

#[derive(Deserialize, Merge)]
pub struct Triplabel {
pub c: Option<String>,
pub n: Option<String>,
pub o: Option<String>,
pub f: Option<String>,

#[serde(rename = "false")]
pub fa: Option<String>,
pub t: Option<String>,
}

#[derive(Deserialize, Merge)]
pub struct Message {
pub id: Option<String>,
pub t: Option<String>,
pub ts: Option<String>,
pub c: Option<String>,
pub cat: Option<String>,
pub del: Option<String>,
pub dm: Option<String>,
pub ec: Option<String>,
pub elnk: Option<String>,
pub ext: Option<String>,
pub from: Option<String>,
pub int: Option<String>,
pub o: Option<String>,
pub pr: Option<String>,
pub tl: Option<Triplabel>,
pub to: Option<String>,
}

#[derive(Deserialize, Merge)]
pub struct Timetable {
pub station: Option<String>,
pub eva: Option<i64>,
pub m: Option<Vec<Message>>,
pub s: Option<Vec<TimetableStop>>,
}
5 changes: 5 additions & 0 deletions src/timetablepresenter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use crate::timetable::Timetable;

pub trait TimetablePresenter {
fn present(&self, timetable: &Timetable);
}
Loading

0 comments on commit 83c57a7

Please sign in to comment.