mirror of
https://github.com/shimunn/gitredditor.git
synced 2023-11-17 18:42:43 +01:00
works
commit is a bit off all operations should be streamed
This commit is contained in:
147
src/main.rs
147
src/main.rs
@@ -2,57 +2,132 @@
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use git2::{Repository, Signature, Time as GitTime};
|
||||
use serde_json::to_string_pretty;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::{create_dir_all, read_to_string, write as fs_write};
|
||||
use std::iter;
|
||||
use std::iter::Chain;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use std::time::UNIX_EPOCH;
|
||||
use std::iter::IntoIterator;
|
||||
|
||||
mod model;
|
||||
mod opts;
|
||||
|
||||
use crate::model::*;
|
||||
use crate::opts::*;
|
||||
|
||||
fn main() {
|
||||
let comments = Comments::for_user("shim__");
|
||||
for comment in comments {
|
||||
dbg!(comment);
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
println!("Hello, world!");
|
||||
let opts = Opts::from_args();
|
||||
let comments = Comments::for_user(&opts.redditor);
|
||||
let all = comments.take(opts.fetch).filter_map(|c| c.ok()).collect::<Vec<_>>();
|
||||
|
||||
println!(
|
||||
"Hello, world! {:?}",
|
||||
update(
|
||||
&opts.repo.unwrap(),
|
||||
&all,
|
||||
&opts.redditor,
|
||||
&("reddit.com/u/".to_owned() + &opts.redditor)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/*fn fetch_comments<'a, T: AsRef<str> + fmt::Display>(
|
||||
redditor: &'a T,
|
||||
from: Option<String>,
|
||||
) -> std::iter::Chain<
|
||||
std::result::Result<
|
||||
std::vec::Vec<model::Comment>,
|
||||
std::boxed::Box<(dyn std::error::Error + 'static)>,
|
||||
>,
|
||||
std::vec::Vec<model::Comment>,
|
||||
> {
|
||||
fn request_paged(url: &str) -> Result<(Vec<Comment>, Option<String>), Box<Error>> {
|
||||
let comment_json = reqwest::get(url)?.text()?;
|
||||
|
||||
let continuation: Option<String> = unimplemented!();
|
||||
let comments: Vec<Comment> = unimplemented!();
|
||||
return Ok((comments, continuation));
|
||||
}
|
||||
let page = request_paged(&format!("https://reddit.com/user/{}.json", redditor));
|
||||
let next: std::iter::Chain<
|
||||
std::result::Result<
|
||||
std::vec::Vec<model::Comment>,
|
||||
std::boxed::Box<(dyn std::error::Error + 'static)>,
|
||||
>,
|
||||
std::vec::Vec<model::Comment>,
|
||||
> = if let Ok((_, cont)) = page {
|
||||
fetch_comments(redditor, cont)
|
||||
} else {
|
||||
unimplemented!() //iter::once(Vec::with_capacity(0).iter()).chain([].into_iter().map(|_| fetch_comments("never_gonna_happen", None)))
|
||||
fn update(
|
||||
repo: &PathBuf,
|
||||
current: &Vec<Comment>,
|
||||
redditor: &str,
|
||||
email: &str,
|
||||
) -> Result<usize, Box<Error>> {
|
||||
let comment_path = |c: &Comment| {
|
||||
let mut p = repo.clone();
|
||||
for s in c.permalink.split("/") {
|
||||
p.push(s);
|
||||
}
|
||||
p.set_extension("json");
|
||||
p
|
||||
};
|
||||
iter::once([()].into_iter().map(|_| page)).chain([()].into_iter().flat_map(|_| next))
|
||||
}*/
|
||||
let mut updated: usize = 0;
|
||||
let git = Repository::open(&repo)?;
|
||||
let sig = Signature::now(redditor, email)?;
|
||||
let mut index = git.index()?;
|
||||
for comment in current.into_iter() {
|
||||
let path = comment_path(&comment);
|
||||
let path_rel = || {
|
||||
let mut p = PathBuf::from(&comment.permalink[1..(comment.permalink.len() - 1)]);
|
||||
p.set_extension("json");
|
||||
p
|
||||
};
|
||||
let before = updated;
|
||||
let mut commit_msg = String::new();
|
||||
if (&path).exists() {
|
||||
let content = read_to_string(&path)?;
|
||||
let old: Comment = serde_json::from_str(&content)?;
|
||||
let delta = CommentDelta::from(&old, &comment);
|
||||
if delta.len() > 0 {
|
||||
fs_write(&path, to_string_pretty(&comment)?)?;
|
||||
commit_msg = delta.iter().map(|d| d.to_string()).collect::<Vec<_>>()[..].join("\n");
|
||||
index.update_all(vec![&path], None)?;
|
||||
|
||||
updated += 1;
|
||||
}
|
||||
} else {
|
||||
create_dir_all((&path).parent().unwrap())?;
|
||||
fs_write(&path, to_string_pretty(&comment)?)?;
|
||||
index.add_all(vec![&path], git2::IndexAddOption::DEFAULT, None)?;
|
||||
commit_msg = CommentDelta::New.to_string();
|
||||
updated += 1;
|
||||
}
|
||||
if before != updated {
|
||||
index.add_path(&path_rel())?;
|
||||
let tree_id = index.write_tree()?;
|
||||
let tree = git.find_tree(tree_id)?;
|
||||
|
||||
let parent = dbg!(git.find_commit(git.head()?.target().unwrap()))?;
|
||||
|
||||
let time = {
|
||||
let created = UNIX_EPOCH + Duration::from_secs(comment.created as u64);
|
||||
let edited = comment
|
||||
.edited
|
||||
.filter(|e| e > &1)
|
||||
.map(|e| UNIX_EPOCH + Duration::from_secs(e));
|
||||
if let Some(edited) = edited {
|
||||
if edited > created {
|
||||
edited
|
||||
} else {
|
||||
created
|
||||
}
|
||||
} else {
|
||||
created
|
||||
}
|
||||
};
|
||||
|
||||
let sig_backdate = Signature::new(
|
||||
sig.name().unwrap(),
|
||||
sig.email().unwrap(),
|
||||
&GitTime::new(
|
||||
dbg!(time).duration_since(UNIX_EPOCH).unwrap().as_secs() as i64,
|
||||
0,
|
||||
),
|
||||
)?;
|
||||
|
||||
println!("Commiting: {}:\n{}", comment.id, commit_msg);
|
||||
git.commit(
|
||||
Some("HEAD"),
|
||||
&sig_backdate,
|
||||
&sig_backdate,
|
||||
&commit_msg,
|
||||
&tree,
|
||||
&[&parent],
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
fn poll(interval: Duration, count: Option<u32>) {
|
||||
let mut it: u32 = 0;
|
||||
|
53
src/model.rs
53
src/model.rs
@@ -2,17 +2,55 @@ use serde::de;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::iter::Chain;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Comment {
|
||||
score: u32,
|
||||
id: String,
|
||||
created: f64,
|
||||
permalink: String,
|
||||
pub score: i32,
|
||||
pub id: String,
|
||||
pub created: f64,
|
||||
pub permalink: String,
|
||||
pub body: String,
|
||||
#[serde(deserialize_with = "false_or_val")]
|
||||
edited: Option<u64>,
|
||||
pub edited: Option<u64>,
|
||||
}
|
||||
|
||||
pub enum CommentDelta {
|
||||
Votes(i32),
|
||||
Content,
|
||||
New,
|
||||
}
|
||||
|
||||
impl fmt::Display for CommentDelta {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use CommentDelta::*;
|
||||
match self {
|
||||
Votes(d) if d > &0 => write!(f, "Received +{} upvotes", d),
|
||||
Votes(d) if d < &0 => write!(f, "Received {} downvotes", d),
|
||||
Votes(_) => write!(f, "You shouln't see this one, if you do check the source"),
|
||||
Content => write!(f, "Edited"),
|
||||
New => write!(f, "Created"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommentDelta {
|
||||
pub fn from(a: &Comment, b: &Comment) -> Vec<CommentDelta> {
|
||||
assert_eq!(a.id, b.id);
|
||||
//Assume that most comments don't change
|
||||
let mut delta = Vec::with_capacity(0);
|
||||
use CommentDelta::*;
|
||||
if a.score != b.score {
|
||||
delta.push(Votes(b.score - a.score))
|
||||
}
|
||||
|
||||
if a.body != b.body {
|
||||
delta.push(Content)
|
||||
}
|
||||
delta
|
||||
}
|
||||
}
|
||||
|
||||
fn false_or_val<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
|
||||
@@ -64,8 +102,7 @@ impl Iterator for Comments {
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
fn request_paged(url: &str) -> Result<(Vec<Comment>, Option<String>), Box<Error>> {
|
||||
dbg!(("Requesting", url));
|
||||
let comment_json = dbg!(reqwest::get(url)?.text()?);
|
||||
let comment_json = reqwest::get(url)?.text()?;
|
||||
let comment_json: Value = serde_json::from_str(&comment_json)?;
|
||||
let data: &Value = &comment_json["data"];
|
||||
let continuation: Option<String> = match &data["after"] {
|
||||
|
15
src/opts.rs
Normal file
15
src/opts.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use std::path::PathBuf;
|
||||
pub use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "gitredditor")]
|
||||
pub struct Opts {
|
||||
#[structopt(short = "f", long = "fetch", default_value = "20")]
|
||||
pub fetch: usize,
|
||||
|
||||
#[structopt(short = "r", long = "redditor")]
|
||||
pub redditor: String,
|
||||
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub repo: Option<PathBuf>,
|
||||
}
|
Reference in New Issue
Block a user