initial commit

This commit is contained in:
2025-11-16 01:25:29 -08:00
commit cf76570fca
4 changed files with 1837 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
*~

1717
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "mapbattle"
version = "0.1.0"
edition = "2024"
[dependencies]
chrono = "0.4.42"
reqwest = "0.12.24"
tokio = { version = "1.48.0", features = ["full"] }
xml = "1.1.0"

108
src/main.rs Normal file
View File

@ -0,0 +1,108 @@
use std::{collections::HashMap, error::Error};
use chrono::NaiveDateTime;
use xml::{attribute::OwnedAttribute, reader::{EventReader, XmlEvent}};
use reqwest::Client;
const DATE_FORMAT: &str = "%Y-%m-%dT%H:%M:%SZ";
#[tokio::main]
async fn main()-> Result<(), Box<dyn Error>> {
let client = Client::builder()
.user_agent("MapBattle/0.1")
.build()?;
let user = "ammaratef45";
let start_date = NaiveDateTime::parse_from_str("2025-11-13T11:55:07Z", DATE_FORMAT)?;
let end_date = NaiveDateTime::parse_from_str("2025-11-13T15:55:07Z", DATE_FORMAT)?;
let body = get_changesets(String::from(user), &client).await?;
let reader = EventReader::from_str(&body);
let changesets = extract_changesets(reader);
let changesets = time_bound(changesets, start_date, end_date);
let points = to_points(changesets, &client).await;
println!("{points:?}");
Ok(())
}
async fn to_points(changesets: Vec<Vec<OwnedAttribute>>, client: &Client) -> HashMap<String, i32> {
let mut result: HashMap<String, i32> = HashMap::new();
for changeset in changesets {
let min_lat = changeset.clone().into_iter().find(|att| att.name.local_name.eq("min_lat")).unwrap().value;
let min_lon = changeset.clone().into_iter().find(|att| att.name.local_name.eq("min_lon")).unwrap().value;
// TODO get the points from the changes_count
let score = changeset.into_iter().find(|att| att.name.local_name.eq("changes_count")).unwrap().value;
let score:i32 = score.parse().expect("changes count returned as not a string");
let terriroty = get_territory(min_lat, min_lon, client).await.expect("failed to get territory");
result.entry(terriroty).and_modify(|val| *val += score).or_insert(score);
}
result
}
fn time_bound(input: Vec<Vec<OwnedAttribute>>, start_date: NaiveDateTime, end_date: NaiveDateTime) -> Vec<Vec<OwnedAttribute>> {
input.into_iter().filter(|list| {
match list.iter().find(|att| att.name.local_name.eq("created_at")) {
Some(created_at) => {
match NaiveDateTime::parse_from_str(&created_at.value, DATE_FORMAT) {
Ok(date) => date > start_date && date < end_date,
Err(_) => {
todo!()
}
}
}
None => false
}
}).collect()
}
fn extract_changesets(reader: EventReader<&[u8]>) -> Vec<Vec<OwnedAttribute>> {
reader.into_iter().filter(|event| match event {
Ok(XmlEvent::StartElement { name, .. }) => {
name.local_name.eq("changeset")
},
Err(_) => todo!(),
_ => false
}).map(|event| match event {
Ok(XmlEvent::StartElement { name:_, attributes, .. }) => attributes,
_ => panic!("We should have already filtered out all of these")
}).collect()
}
async fn get_changesets(user: String, client: &Client) -> Result<String, Box<dyn Error>> {
let endpoint = format!("https://api.openstreetmap.org/api/0.6/changesets?display_name={user}");
get_data(endpoint, client).await
}
async fn get_territory(lat: String, lon: String, client: &Client) -> Result<String, Box<dyn Error>> {
let endpoint = format!("https://nominatim.openstreetmap.org/reverse??format=json&lon={lon}&lat={lat}");
let resp = get_data(endpoint, client).await?;
let reader = EventReader::from_str(&resp);
Ok(extract_tag_value(reader, String::from("suburb")).unwrap())
}
async fn get_data(endpoint: String, client: &Client) -> Result<String, Box<dyn Error>> {
Ok(client.get(endpoint).send().await?.text().await?)
}
fn extract_tag_value(reader: EventReader<&[u8]>, tag: String) -> Option<String> {
let mut inside_tag = false;
for e in reader {
match e {
Ok(XmlEvent::StartElement { name, .. }) => {
if name.local_name == tag {
inside_tag = true;
}
}
Ok(XmlEvent::Characters(data)) => {
if inside_tag {
return Some(data);
}
}
Ok(XmlEvent::EndElement { name }) => {
if name.local_name == tag {
inside_tag = false;
}
}
_ => {}
}
}
None
}