Skip to content

Commit

Permalink
split lib.rs into 3 files
Browse files Browse the repository at this point in the history
  • Loading branch information
figsoda committed Oct 25, 2022
1 parent 35bf9b3 commit 376c909
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 149 deletions.
55 changes: 55 additions & 0 deletions src/course.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use eyre::{eyre, Result, WrapErr};
use icalendar::parser::{read_calendar_simple, unfold};

use crate::{Props, PropsExt};

pub fn get_course_url(props: &Props) -> Result<String> {
let proj = format!(
"{} project {}: ",
props.get_prop("courseName")?,
props.get_prop("projectNumber")?,
);

read_calendar_simple(&unfold(
&ureq::get(&format!(
"{}/feed/CourseCalendar?courseKey={}",
props.get_prop("baseURL")?,
props.get_prop("courseKey")?,
))
.call()
.wrap_err("failed to download the course calendar")?
.into_string()
.wrap_err("failed to parse the course calendar")?,
))
.map_err(|e| eyre!("{e}").wrap_err("failed to parse the course calendar"))?
.get(0)
.and_then(|root| {
root.components.iter().find_map(|component| {
let mut url = None;
let mut found = false;

for prop in component.properties.iter() {
match prop.name.as_str() {
"SUMMARY" => {
if prop.val.as_str().starts_with(&proj) {
found = true;
} else {
return None;
}
}

"URL" => url = Some(prop.val.to_string()),

_ => {}
}
}

if found {
url
} else {
None
}
})
})
.ok_or_else(|| eyre!("failed to find the course url"))
}
152 changes: 4 additions & 148 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ mod auth;
mod cli;
mod cmd;
pub mod config;
pub mod course;
mod cred;
pub mod state;
mod submit;

use eyre::{eyre, Result, WrapErr};
use icalendar::parser::{read_calendar_simple, unfold};
use multipart::client::lazy::Multipart;
use eyre::{eyre, Result};

use std::{collections::HashMap, io::Write};

use crate::{config::Config, state::State};
use std::collections::HashMap;

type Props = HashMap<String, String>;

Expand All @@ -27,145 +25,3 @@ impl PropsExt for Props {
.ok_or_else(|| eyre!("{key} is null in .submit"))
}
}

impl<W: Write> State<W> {
pub fn submit(
&mut self,
user_props: Props,
props: &Props,
cfg: &Config,
zip: &[u8],
) -> Result<()> {
self.run_hook(&cfg.pre_submit_hook, "pre-submit")?;
self.submit_project(user_props, props, cfg, zip, true)?;
self.run_hook(&cfg.post_submit_hook, "post-submit")?;

Ok(())
}

fn submit_project(
&mut self,
user_props: Props,
props: &Props,
opts: &Config,
zip: &[u8],
reauth: bool,
) -> Result<()> {
if reauth
&& (!user_props.contains_key("cvsAccount") && !user_props.contains_key("classAccount")
|| !user_props.contains_key("oneTimePassword"))
{
let user_props = self.negotiate_otp(props, opts)?;
return self.submit_project(user_props, props, opts, zip, false);
}

let mut parts = Multipart::new();

for (k, v) in user_props.iter().chain(props) {
parts.add_text(k.clone(), v);
}

let parts = parts
.add_text("submitClientTool", "sagoin")
.add_text("submitClientVersion", env!("CARGO_PKG_VERSION"))
.add_stream(
"submittedFiles",
zip,
Some("submit.zip"),
Some(
"application/zip"
.parse()
.wrap_err("failed to parse application/zip as a mime type")?,
),
)
.prepare()?;

match ureq::post(props.get_prop("submitURL")?)
.set(
"Content-Type",
&format!("multipart/form-data; boundary={}", parts.boundary()),
)
.send(parts)
{
Ok(resp) => {
if let Ok(success) = resp.into_string() {
write!(self.out, "{success}")?;
} else {
writeln!(self.out, "Successful submission received")?;
}

Ok(())
}

Err(ureq::Error::Status(500, resp)) if reauth => {
warn!(self, "Status code 500");
if let Ok(err) = resp.into_string() {
warn!(self, "{err}");
}
let user_props = self.negotiate_otp(props, opts)?;
self.submit_project(user_props, props, opts, zip, false)
}

Err(ureq::Error::Status(code, resp)) => Err(if let Ok(err) = resp.into_string() {
eyre!("{}", err.trim_end())
.wrap_err(format!("status code {code}"))
.wrap_err("failed to submit project")
} else {
eyre!("status code {code}").wrap_err("failed to submit project")
}),

Err(e) => Err(e).wrap_err("failed to send request to the submit server"),
}
}
}

pub fn get_course_url(props: &Props) -> Result<String> {
let proj = format!(
"{} project {}: ",
props.get_prop("courseName")?,
props.get_prop("projectNumber")?,
);

read_calendar_simple(&unfold(
&ureq::get(&format!(
"{}/feed/CourseCalendar?courseKey={}",
props.get_prop("baseURL")?,
props.get_prop("courseKey")?,
))
.call()
.wrap_err("failed to download the course calendar")?
.into_string()
.wrap_err("failed to parse the course calendar")?,
))
.map_err(|e| eyre!("{e}").wrap_err("failed to parse the course calendar"))?
.get(0)
.and_then(|root| {
root.components.iter().find_map(|component| {
let mut url = None;
let mut found = false;

for prop in component.properties.iter() {
match prop.name.as_str() {
"SUMMARY" => {
if prop.val.as_str().starts_with(&proj) {
found = true;
} else {
return None;
}
}

"URL" => url = Some(prop.val.to_string()),

_ => {}
}
}

if found {
url
} else {
None
}
})
})
.ok_or_else(|| eyre!("failed to find the course url"))
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{
io::{self, Cursor},
};

use sagoin::{config::load_config, get_course_url};
use sagoin::{config::load_config, course::get_course_url};

fn main() -> Result<()> {
let (cfg, mut state) = load_config()?;
Expand Down
97 changes: 97 additions & 0 deletions src/submit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::io::Write;

use eyre::{eyre, Result, WrapErr};
use multipart::client::lazy::Multipart;

use crate::{config::Config, state::State, warn, Props, PropsExt};

impl<W: Write> State<W> {
pub fn submit(
&mut self,
user_props: Props,
props: &Props,
cfg: &Config,
zip: &[u8],
) -> Result<()> {
self.run_hook(&cfg.pre_submit_hook, "pre-submit")?;
self.submit_project(user_props, props, cfg, zip, true)?;
self.run_hook(&cfg.post_submit_hook, "post-submit")?;

Ok(())
}

fn submit_project(
&mut self,
user_props: Props,
props: &Props,
opts: &Config,
zip: &[u8],
reauth: bool,
) -> Result<()> {
if reauth
&& (!user_props.contains_key("cvsAccount") && !user_props.contains_key("classAccount")
|| !user_props.contains_key("oneTimePassword"))
{
let user_props = self.negotiate_otp(props, opts)?;
return self.submit_project(user_props, props, opts, zip, false);
}

let mut parts = Multipart::new();

for (k, v) in user_props.iter().chain(props) {
parts.add_text(k.clone(), v);
}

let parts = parts
.add_text("submitClientTool", "sagoin")
.add_text("submitClientVersion", env!("CARGO_PKG_VERSION"))
.add_stream(
"submittedFiles",
zip,
Some("submit.zip"),
Some(
"application/zip"
.parse()
.wrap_err("failed to parse application/zip as a mime type")?,
),
)
.prepare()?;

match ureq::post(props.get_prop("submitURL")?)
.set(
"Content-Type",
&format!("multipart/form-data; boundary={}", parts.boundary()),
)
.send(parts)
{
Ok(resp) => {
if let Ok(success) = resp.into_string() {
write!(self.out, "{success}")?;
} else {
writeln!(self.out, "Successful submission received")?;
}

Ok(())
}

Err(ureq::Error::Status(500, resp)) if reauth => {
warn!(self, "Status code 500");
if let Ok(err) = resp.into_string() {
warn!(self, "{err}");
}
let user_props = self.negotiate_otp(props, opts)?;
self.submit_project(user_props, props, opts, zip, false)
}

Err(ureq::Error::Status(code, resp)) => Err(if let Ok(err) = resp.into_string() {
eyre!("{}", err.trim_end())
.wrap_err(format!("status code {code}"))
.wrap_err("failed to submit project")
} else {
eyre!("status code {code}").wrap_err("failed to submit project")
}),

Err(e) => Err(e).wrap_err("failed to send request to the submit server"),
}
}
}

0 comments on commit 376c909

Please sign in to comment.