├── .gitignore ├── src ├── lib.rs ├── init.rs ├── gradingtable.rs ├── grade.rs ├── args.rs ├── main.rs ├── unpack.rs ├── config.rs ├── repack.rs └── fetch.rs ├── Dockerfile ├── .pre-commit-config.yaml ├── .gitlab-ci.yml ├── Cargo.toml ├── readme.md ├── license └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod args; 2 | pub mod config; 3 | pub mod fetch; 4 | pub mod grade; 5 | pub mod gradingtable; 6 | pub mod init; 7 | pub mod repack; 8 | pub mod unpack; 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:latest 2 | 3 | RUN apt update -y && \ 4 | apt upgrade -y && \ 5 | apt install -y g++-mingw-w64-x86-64 6 | 7 | RUN rustup target add x86_64-pc-windows-gnu && \ 8 | rustup target add x86_64-unknown-linux-gnu 9 | -------------------------------------------------------------------------------- /src/init.rs: -------------------------------------------------------------------------------- 1 | use crate::config::*; 2 | use std::{error::Error, fs::*, path::PathBuf}; 3 | 4 | pub fn init_master(cfg: &MasterCfg) -> Result<(), Box> { 5 | let cfg_path: PathBuf = MASTER_CFG_FILENAME.into(); 6 | if cfg_path.is_file() { 7 | return Err(format!("{} already exists!", MASTER_CFG_FILENAME).into()); 8 | } 9 | 10 | write(cfg_path, toml::to_string_pretty(cfg)?)?; 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.4.0 6 | hooks: 7 | - id: trailing-whitespace 8 | exclude: \.rs$ 9 | - id: end-of-file-fixer 10 | exclude: \.rs$ 11 | - repo: https://github.com/doublify/pre-commit-rust 12 | rev: v1.0 13 | hooks: 14 | - id: fmt 15 | - id: cargo-check 16 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: "registry.git.rwth-aachen.de/georgar/kasm:latest" 2 | 3 | cache: 4 | paths: 5 | - target/ 6 | 7 | build-linux64: 8 | stage: build 9 | script: 10 | - cargo build -r --target=x86_64-unknown-linux-gnu 11 | artifacts: 12 | name: kasm.linux64-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} 13 | expose_as: "kasm-linux64" 14 | paths: 15 | - target/x86_64-unknown-linux-gnu/release/kasm 16 | 17 | build-win64: 18 | stage: build 19 | script: 20 | - cargo build -r --target=x86_64-pc-windows-gnu 21 | artifacts: 22 | name: kasm.win64-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA} 23 | expose_as: "kasm-win64" 24 | paths: 25 | - target/x86_64-pc-windows-gnu/release/kasm.exe 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kasm" 3 | description = "kasm (is) Another Submission Multitool" 4 | version = "1.0.0" 5 | edition = "2021" 6 | authors = ["kernzerfall "] 7 | license = "EUPL-1.2-or-later" 8 | 9 | [profile.release] 10 | strip = true 11 | opt-level = "z" # We can afford -Oz since the binary is "fast enough" anyway 12 | lto = true 13 | 14 | [dependencies] 15 | clap = { version = "4.2.7", features = ["derive"] } 16 | csv = "1.2.1" 17 | inquire = "0.6.2" 18 | keyring = "2.0.2" 19 | log = "0.4.17" 20 | pretty_env_logger = "0.5.0" 21 | regex = "1.8.1" 22 | reqwest = { version = "0.11.17", features = ["blocking"] } 23 | serde = { version = "1.0.162", features = ["serde_derive", "derive"] } 24 | serde_json = "1.0.96" 25 | strum = { version = "0.25", features = ["derive"] } 26 | toml = "0.8" 27 | whoami = "1.4.0" 28 | zip = { version = "0.6.4", default-features = false, features = ["deflate", "time"] } 29 | -------------------------------------------------------------------------------- /src/gradingtable.rs: -------------------------------------------------------------------------------- 1 | use log::trace; 2 | use std::{error::Error, path::PathBuf}; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Debug, Clone, Serialize, Deserialize)] 7 | pub struct GradingRecord { 8 | /// Internal moodle id 9 | #[serde(rename = "ID")] 10 | pub internal_id: String, 11 | 12 | /// Full name 13 | #[serde(rename = "Vollständiger Name")] 14 | pub name: String, 15 | 16 | /// University ID Number (MatrNr.) 17 | #[serde(rename = "Matrikelnummer")] 18 | pub uni_id: String, 19 | 20 | #[serde(rename = "Status")] 21 | pub status: String, 22 | 23 | /// Full group name 24 | #[serde(rename = "Gruppe")] 25 | pub group: String, 26 | 27 | // Other fields 28 | #[serde(rename = "Bewertung")] 29 | pub grade: String, 30 | 31 | #[serde(rename = "Bestwertung")] 32 | pub best_grade: String, 33 | 34 | #[serde(rename = "Bewertung kann geändert werden")] 35 | pub grade_locked: String, 36 | 37 | #[serde(rename = "Zuletzt geändert (Abgabe)")] 38 | pub last_change_submission: String, 39 | 40 | #[serde(rename = "Zuletzt geändert (Bewertung)")] 41 | pub last_change_grade: String, 42 | 43 | #[serde(rename = "Feedback als Kommentar")] 44 | pub feedback_comment: String, 45 | } 46 | 47 | impl GradingRecord { 48 | pub fn from_csv(path: &PathBuf) -> Result, Box> { 49 | if !path.is_file() { 50 | return Err("could not find csv".into()); 51 | } 52 | 53 | let file = std::fs::File::open(path)?; 54 | let mut reader = csv::Reader::from_reader(file); 55 | Ok(reader 56 | .deserialize() 57 | .filter_map(|result| { 58 | trace!("checking {:?}", result); 59 | result.ok() 60 | }) 61 | .collect::>()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/grade.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use crate::args::GradeCmd; 4 | use crate::config::{Grades, MasterCfg}; 5 | use log::{error, info}; 6 | 7 | pub fn grade(master: &MasterCfg, cfg: &GradeCmd, grades: &Grades) -> Result<(), Box> { 8 | let reg = regex::Regex::new(&master.groups_regex)?; 9 | 10 | let target = match &cfg.target { 11 | Some(str) => str.clone(), 12 | None => { 13 | let cd = std::env::current_dir()?; 14 | let infer = cd.components().find_map(|c| { 15 | reg.captures(c.as_os_str().to_str().unwrap()) 16 | .and_then(|caps| caps.get(2).map(|c| c.as_str())) 17 | }); 18 | 19 | if let Some(infer) = infer { 20 | info!("inferred group {} based on path", infer); 21 | infer.to_string() 22 | } else { 23 | error!("you didn't specify the group to be graded and it couldn't be inferred"); 24 | return Err("".into()); 25 | } 26 | } 27 | }; 28 | 29 | info!("grading {} with {}", target, cfg.grade); 30 | 31 | let mut grades = grades.clone(); 32 | let mut changed = false; 33 | grades 34 | .map 35 | .iter_mut() 36 | .find(|gd| { 37 | reg.captures(&gd.target).map_or(false, |caps| { 38 | caps.get(2).map_or(false, |cap| cap.as_str() == target) 39 | }) 40 | }) 41 | .map(|gd| { 42 | info!("found match"); 43 | changed = true; 44 | gd.grade = cfg.grade.to_owned(); 45 | Some(()) 46 | }) 47 | .or_else(|| { 48 | error!("no matching group found!"); 49 | None 50 | }); 51 | 52 | if changed { 53 | info!("writing grades"); 54 | std::fs::write(grades.location.clone(), toml::to_string_pretty(&grades)?)?; 55 | } 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use crate::config::MasterCfg; 2 | use clap::*; 3 | use std::path::PathBuf; 4 | 5 | #[derive(Parser, Clone, Debug, Default)] 6 | pub struct UnpackFiles { 7 | #[arg(short, long = "sheet", value_name = "sheet")] 8 | pub sheet_id: String, 9 | 10 | /// Path to the .zip downloaded from moodle 11 | #[arg(short = 'z', long, value_name = "/path/to/zip")] 12 | pub moodle_zip: PathBuf, 13 | 14 | /// Path to the .csv downloaded from moodle 15 | #[arg(short = 'c', long, value_name = "/path/to/csv")] 16 | pub moodle_csv: PathBuf, 17 | } 18 | 19 | #[derive(Parser, Clone, Debug, Default)] 20 | pub struct RepackDir { 21 | /// Sheet ID 22 | #[arg(value_name = "sheet")] 23 | pub sheet_id: String, 24 | } 25 | 26 | #[derive(Parser, Clone, Debug, Default)] 27 | pub struct FetchCmd { 28 | /// Sheet ID 29 | #[arg(value_name = "sheet")] 30 | pub sheet_id: Option, 31 | } 32 | 33 | /// Grade Command struct. Identical to config::Grade, but 34 | /// kept separate due to semantical differences between 35 | /// the target variables. 36 | #[derive(Parser, Clone, Debug, Default)] 37 | pub struct GradeCmd { 38 | /// The grade to assign the group, as Moodle wants it 39 | /// 40 | /// e.g. 10,5 or 10,0 41 | #[arg(value_name = "grade")] 42 | pub grade: String, 43 | 44 | /// ID of the group/person to grade. 45 | /// 46 | /// Inferred if ommitted. 47 | /// Matched by the 2nd regex capture group! 48 | /// 49 | /// e.g. 04 or K or 01 50 | // note that this MUST come second if we want to omit/infer it... 51 | #[arg(value_name = "target_team")] 52 | pub target: Option, 53 | } 54 | 55 | /// Push Command Struct. Basically tells us whether we're dry-running. 56 | #[derive(Parser, Clone, Debug, Default)] 57 | pub struct PushCmd { 58 | #[arg(long = "dry-run", default_value_t = false)] 59 | pub dry_run: bool, 60 | } 61 | 62 | /// First subcommand ("verb") found on the cmdline 63 | #[derive(Subcommand, Clone, Debug)] 64 | pub enum Verb { 65 | /// Unpack/filter moodle zip using a csv file 66 | Unpack(UnpackFiles), 67 | /// Repack the zip to publish feedback/grades 68 | Repack(RepackDir), 69 | /// Initialize the master config file 70 | Init(MasterCfg), 71 | /// Grade team 72 | Grade(GradeCmd), 73 | /// Set up autofetch 74 | SetupFetch, 75 | /// Fetch assignment submissions 76 | Fetch(FetchCmd), 77 | /// Push grades to moodle 78 | Push(PushCmd), 79 | } 80 | 81 | #[derive(Parser, Clone, Debug)] 82 | #[command(author, version, about, next_line_help = true)] 83 | pub struct Cli { 84 | #[command(subcommand)] 85 | pub verb: Verb, 86 | } 87 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use kasm::config::Structure; 3 | use kasm::config::UNPACK_PATH_FILENAME_BASE; 4 | use kasm::grade::grade; 5 | use kasm::repack::repack; 6 | use kasm::unpack::unpack; 7 | 8 | use kasm::args::Verb; 9 | use kasm::config::Grades; 10 | use kasm::config::MasterCfg; 11 | use kasm::config::UNPACK_GRADES_FILENAME; 12 | use log::{error, info}; 13 | 14 | const DEF_LOG_LEVEL: &str = "info"; 15 | const ENV_LOG_LEVEL: &str = "RUST_LOG"; 16 | 17 | fn main() { 18 | if std::env::var(ENV_LOG_LEVEL).is_err() { 19 | std::env::set_var(ENV_LOG_LEVEL, DEF_LOG_LEVEL); 20 | } 21 | pretty_env_logger::init(); 22 | let command = kasm::args::Cli::parse(); 23 | 24 | if let Verb::Init(ref cfg) = command.verb { 25 | kasm::init::init_master(cfg).unwrap(); 26 | return; 27 | } 28 | 29 | let master = match MasterCfg::resolve() { 30 | Ok(cfg) => cfg, 31 | Err(e) => { 32 | error!("MasterCfg::resolve() returned error {:?}", e); 33 | error!("could not read config. run \"kasm init\" first!"); 34 | panic!() 35 | } 36 | }; 37 | 38 | if master.unpack_structure != Structure::Groups { 39 | panic!("Only unpack_structure = \"Groups\" is currently supported."); 40 | } 41 | 42 | if master.recursive_unzip { 43 | error!("recursive unzip is not implemented yet, continuing"); 44 | } 45 | 46 | let grades = Grades::resolve(); 47 | 48 | match command.verb { 49 | Verb::Unpack(cfg) => { 50 | unpack(&master, &cfg).unwrap(); 51 | } 52 | Verb::Grade(cfg) => { 53 | if let Ok(grades) = grades { 54 | grade(&master, &cfg, &grades).unwrap() 55 | } else { 56 | error!("{} could not be found!", UNPACK_GRADES_FILENAME); 57 | } 58 | } 59 | Verb::Repack(cfg) => { 60 | repack(&master, &cfg).unwrap(); 61 | } 62 | Verb::SetupFetch => { 63 | kasm::fetch::setup(&master).unwrap(); 64 | } 65 | Verb::Fetch(_cfg) => { 66 | kasm::fetch::MoodleFetcher::new(&master) 67 | .interactive_dl() 68 | .unwrap(); 69 | } 70 | Verb::Push(cfg) => { 71 | if let Ok(ref grades) = grades { 72 | kasm::fetch::MoodleFetcher::new(&master) 73 | .push_grades(grades, cfg.dry_run) 74 | .unwrap(); 75 | } else { 76 | error!("{} could not be found!", UNPACK_GRADES_FILENAME); 77 | info!( 78 | "run this command from inside an {}xx directory", 79 | UNPACK_PATH_FILENAME_BASE 80 | ); 81 | } 82 | } 83 | _ => panic!("unexpected verb"), 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/unpack.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::error::Error; 3 | use std::path::Path; 4 | use std::path::PathBuf; 5 | 6 | use crate::args::UnpackFiles; 7 | use crate::config::Grades; 8 | use crate::config::MasterCfg; 9 | 10 | use crate::gradingtable::GradingRecord; 11 | use log::{error, info, trace}; 12 | use regex::Regex; 13 | 14 | use crate::config::Grade; 15 | use crate::config::Structure; 16 | use crate::config::UNPACK_CSV_FILENAME; 17 | use crate::config::UNPACK_GRADES_FILENAME; 18 | use crate::config::UNPACK_PATH_FILENAME_BASE; 19 | 20 | pub fn unpack(master: &MasterCfg, cfg: &UnpackFiles) -> Result<(), Box> { 21 | let unpack_path: PathBuf = (UNPACK_PATH_FILENAME_BASE.to_owned() + &cfg.sheet_id).into(); 22 | if unpack_path.is_dir() { 23 | error!("unpack path {:?} already exists!", unpack_path); 24 | return Err("".into()); 25 | } 26 | 27 | info!("creating dir {:?}", unpack_path); 28 | std::fs::create_dir_all(unpack_path.clone())?; 29 | 30 | let reg = regex::Regex::new(&master.groups_regex)?; 31 | let records = GradingRecord::from_csv(&cfg.moodle_csv.clone()).expect("gradingtable csv"); 32 | 33 | info!("csv has {} records", records.len()); 34 | let filtered = records 35 | .iter() 36 | .filter(|&r| { 37 | reg.captures(&r.group).map_or(false, |caps| { 38 | caps.get(1) 39 | .map_or(false, |val| val.as_str() == master.group) 40 | }) 41 | }) 42 | .collect::>(); 43 | 44 | if filtered.is_empty() { 45 | error!( 46 | "could not find any records matching master.group = {}", 47 | master.group 48 | ); 49 | return Err("".into()); 50 | } 51 | 52 | info!( 53 | "found {} records matching master.group = {}", 54 | filtered.len(), 55 | master.group 56 | ); 57 | 58 | let gids = unzip_filter_main(master, cfg, ®, &unpack_path)?; 59 | gen_grading_files(master, cfg, &unpack_path, filtered, &gids)?; 60 | 61 | Ok(()) 62 | } 63 | 64 | fn gen_grading_files( 65 | master: &MasterCfg, 66 | cfg: &UnpackFiles, 67 | unpack_path: &Path, 68 | filtered: Vec<&GradingRecord>, 69 | gids: &HashMap, 70 | ) -> Result<(), Box> { 71 | let nested_csv_path = unpack_path.join(UNPACK_CSV_FILENAME); 72 | let mut grades_arr: Vec = Vec::new(); 73 | let mut seen: Vec = Vec::new(); 74 | let nested_csv = std::fs::File::create(nested_csv_path)?; 75 | 76 | info!("writing filtered csv"); 77 | let mut wtr = csv::Writer::from_writer(nested_csv); 78 | filtered.iter().for_each(|&r| { 79 | trace!("serializing {:?}", r); 80 | wtr.serialize(r).unwrap(); 81 | 82 | if !seen.contains(&r.group) { 83 | seen.push(r.group.clone()); 84 | 85 | if master.unpack_structure == Structure::Groups { 86 | grades_arr.push(Grade { 87 | target: r.group.to_owned(), 88 | members: None, 89 | internal_id: gids.get(&r.group).cloned(), 90 | grade: r.grade.to_owned(), 91 | }) 92 | } 93 | } 94 | 95 | if master.unpack_structure == Structure::Individuals { 96 | grades_arr.push(Grade { 97 | target: r.uni_id.to_owned(), 98 | members: None, 99 | internal_id: r.internal_id.to_owned().into(), 100 | grade: r.best_grade.to_owned(), 101 | }); 102 | } 103 | }); 104 | wtr.flush()?; 105 | 106 | info!("saw {} discreet groups", seen.len()); 107 | 108 | let grades_toml_path = unpack_path.join(UNPACK_GRADES_FILENAME); 109 | info!("writing grades.toml"); 110 | std::fs::write( 111 | grades_toml_path.clone(), 112 | toml::to_string_pretty(&Grades { 113 | location: grades_toml_path, 114 | map: grades_arr, 115 | sheet_id: cfg.sheet_id.to_owned(), 116 | source: crate::config::Source::CsvAndZip, 117 | assign_id: None, 118 | })?, 119 | )?; 120 | 121 | Ok(()) 122 | } 123 | 124 | fn unzip_filter_main( 125 | master: &MasterCfg, 126 | cfg: &UnpackFiles, 127 | reg: &Regex, 128 | unpack_path: &Path, 129 | ) -> Result, Box> { 130 | info!("unzipping main zip file"); 131 | 132 | let mut res = HashMap::::new(); 133 | let file = std::fs::File::open(&cfg.moodle_zip)?; 134 | let mut archive = zip::ZipArchive::new(file)?; 135 | 136 | for i in 0..archive.len() { 137 | let mut curr = archive.by_index(i)?; 138 | let curr_name = curr.name(); 139 | 140 | if reg.captures(curr_name).map_or(false, |caps| { 141 | caps.get(1) 142 | .map_or(false, |cap| cap.as_str() == master.group) 143 | }) { 144 | let enclosed_path = curr.enclosed_name().unwrap(); 145 | let mut parts = enclosed_path 146 | .components() 147 | .next() 148 | .unwrap() 149 | .as_os_str() 150 | .to_str() 151 | .unwrap() 152 | .splitn(3, '_'); 153 | let subdir = parts.next().unwrap(); 154 | let groupid = parts.next().unwrap(); 155 | 156 | res.insert(subdir.to_string(), groupid.to_string()); 157 | 158 | let extr = unpack_path 159 | .join(subdir) 160 | .join(enclosed_path.file_name().unwrap()); 161 | std::fs::create_dir_all(extr.parent().unwrap())?; 162 | let mut target = std::fs::File::create(extr)?; 163 | 164 | std::io::copy(&mut curr, &mut target)?; 165 | }; 166 | } 167 | 168 | Ok(res) 169 | } 170 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, path::PathBuf}; 2 | 3 | use clap::*; 4 | use log::debug; 5 | use serde::{Deserialize, Serialize}; 6 | use strum::Display; 7 | 8 | use crate::gradingtable::GradingRecord; 9 | 10 | pub const MASTER_CFG_FILENAME: &str = "kasm.toml"; 11 | pub const DEFAULT_GROUPS_REGEX: &str = r#"([0-9]{2}).+([0-9]{2})"#; 12 | pub const UNPACK_PATH_FILENAME_BASE: &str = "unpack_"; 13 | pub const UNPACK_CSV_FILENAME: &str = ".filtered.csv"; 14 | pub const UNPACK_GRADES_FILENAME: &str = "grades.toml"; 15 | 16 | /// Tells us whether the zip we're extracting contains groupped or individual 17 | /// submissions, as well as whether we want to repack it as one or the other. 18 | #[derive(ValueEnum, Clone, Debug, Default, Display, Serialize, Deserialize, PartialEq, Eq)] 19 | pub enum Structure { 20 | #[default] 21 | Groups, 22 | Individuals, 23 | } 24 | 25 | /// Definition of the master config file 26 | /// (default: kasm.toml) 27 | #[derive(Parser, Clone, Debug, Default, Serialize, Deserialize)] 28 | pub struct MasterCfg { 29 | #[serde(skip)] 30 | #[clap(skip)] 31 | pub location: PathBuf, 32 | 33 | #[serde(skip)] 34 | #[clap(skip)] 35 | pub moodle_token: String, 36 | 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | #[arg(long = "course-id", value_name = "12345678")] 39 | pub moodle_course_id: Option, 40 | 41 | /// Regex with for (group, team) 42 | #[arg(short = 'r', long = "regex", value_name = "expr", default_value = DEFAULT_GROUPS_REGEX)] 43 | pub groups_regex: String, 44 | 45 | // Your exercise group number as shown in Moodle 46 | #[arg(short = 'g', long = "group", value_name = "group id")] 47 | pub group: String, 48 | 49 | /// Unzip nested zip files 50 | #[arg(long, default_value_t = false)] 51 | pub recursive_unzip: bool, 52 | 53 | /// Regex for files to repack 54 | #[arg(short = 'f', long, value_name = "expr")] 55 | pub repack_filter: Option, 56 | 57 | #[arg(long, value_name = "struct", default_value = "groups")] 58 | pub unpack_structure: Structure, 59 | 60 | #[arg(long, value_name = "struct", default_value = "groups")] 61 | pub repack_structure: Structure, 62 | } 63 | 64 | /// Where the files came from 65 | #[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)] 66 | pub enum Source { 67 | #[default] 68 | CsvAndZip, 69 | Autofetch, 70 | } 71 | 72 | /// Nested grades.toml definition 73 | #[derive(Clone, Debug, Default, Serialize, Deserialize)] 74 | pub struct Grades { 75 | /// We save internally where we found the file so 76 | /// that we don't need to search for it again when 77 | /// we want to overwrite it later. 78 | #[serde(skip)] 79 | pub location: PathBuf, 80 | 81 | /// Moodle Assignment ID. Used in autofetch workflows to push grades. 82 | #[serde(skip_serializing_if = "Option::is_none")] 83 | pub assign_id: Option, 84 | 85 | /// Indicates the source of the files. 86 | /// Currently it's impossible to mix and match autofetch 87 | /// and .zips .csvs. 88 | pub source: Source, 89 | 90 | /// Sheed Identificator 91 | /// e.g. 04 92 | pub sheet_id: String, 93 | 94 | /// Grade maps 95 | /// target (matrnr/group_id) -> grade 96 | pub map: Vec, 97 | } 98 | 99 | impl Grades { 100 | // Generates a vector of grading records from the filtered csv 101 | // for the given group of students by overwriting grades using 102 | // the current object's grades 103 | pub fn collect_students_for_group( 104 | &self, 105 | gt: &[GradingRecord], 106 | group_id: &str, 107 | ) -> Vec { 108 | gt.to_owned() 109 | .iter() 110 | .filter(|&gr| gr.group == group_id) 111 | .filter_map(|gr| { 112 | let mut new_gr = gr.clone(); 113 | new_gr.grade = self.find_grade_for_target(group_id)?; 114 | Some(new_gr) 115 | }) 116 | .collect() 117 | } 118 | 119 | pub fn find_grade_for_target(&self, target: &str) -> Option { 120 | self.map 121 | .iter() 122 | .find(|&g| g.target == target) 123 | .map(|g| g.grade.clone()) 124 | } 125 | } 126 | 127 | #[derive(Clone, Debug, Default, Serialize, Deserialize)] 128 | pub struct Grade { 129 | /// Full team name/matrnr that the grade corresponds to 130 | pub target: String, 131 | 132 | /// Internal Moodle ID 133 | #[serde(skip_serializing_if = "Option::is_none")] 134 | pub internal_id: Option, 135 | 136 | /// Member `userid`s to push grades to. Applies only to auto-workflow 137 | #[serde(skip_serializing_if = "Option::is_none")] 138 | pub members: Option>, 139 | 140 | /// The grade to assign the group, should be formatted 141 | /// as Moodle wants it to be formatted 142 | /// e.g. 10,5 or 10,0 143 | pub grade: String, 144 | } 145 | 146 | /// Walks upwards the directory tree and tries to find `filename` 147 | fn find_in_preceding_dir_tree(filename: &str) -> Result> { 148 | let mut path = std::env::current_dir()?; 149 | 150 | while !path.join(filename).is_file() { 151 | if let Some(parent) = path.parent() { 152 | path = parent.to_path_buf(); 153 | } else { 154 | return Err("couldn't find file".into()); 155 | } 156 | } 157 | 158 | debug!("found {:?} under {:?}", filename, path); 159 | Ok(path.join(filename)) 160 | } 161 | 162 | impl MasterCfg { 163 | /// Finds/parses the master config 164 | pub fn resolve() -> Result> { 165 | let cfg_path = find_in_preceding_dir_tree(MASTER_CFG_FILENAME)?; 166 | let mut cfg = toml::from_str::(&std::fs::read_to_string(cfg_path.clone())?)?; 167 | cfg.location = cfg_path; 168 | Ok(cfg) 169 | } 170 | } 171 | 172 | impl Grades { 173 | /// Finds/parses the nested grades config 174 | pub fn resolve() -> Result> { 175 | let cfg_path = find_in_preceding_dir_tree(UNPACK_GRADES_FILENAME)?; 176 | let mut cfg = toml::from_str::(&std::fs::read_to_string(cfg_path.clone())?)?; 177 | cfg.location = cfg_path; 178 | Ok(cfg) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #
kasm
2 |
3 | kasm (is) Another Submission Multitool (for Moodle). Pronounced chasm. Made for terminal warriors. 4 |
5 | 6 |
7 |
8 | 9 |
10 | 11 | 12 | 13 | 14 | ## Installation 15 | Just grab a release for your OS and put it in a path covered by `$PATH` (or equiv.) 16 | e.g. via 17 | 18 | ```bash 19 | sudo install -Dm 0755 kasm /usr/local/bin/kasm 20 | ``` 21 | 22 | Alternatively, grab the source and build/install it with cargo: 23 | ```bash 24 | cargo install --locked --path . 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | ### Video Examples 31 | 32 | - [Setting up (autofetch)](https://s.georgar.de/kasm/00-autofetch-setup.webm) 33 | - [Fetching assignments](https://s.georgar.de/kasm/01-autofetch-downloading-sheets.webm) 34 | - [Grading](https://s.georgar.de/kasm/02-autofetch-grade.webm) 35 | - [Publishing feedback and grades](https://s.georgar.de/kasm/03-autofetch-publish.webm) 36 | 37 | ### Brief overview 38 | - The initialization procedure creates a reusable master config, which contains info such as 39 | - Your exercise group id 40 | - The regex to match groups against 41 | - The structure of the zip (are we expecting groupped folders in it?) 42 | - A filter (regex) to only repack certain files. 43 | 44 | You then have the choice between two workflows. 45 | 46 | **1. CSV & ZIP Workflow** 47 | - In the master directory, you create a slave by `kasm unpack`. It contains 48 | - A slave config (`grades.toml`) 49 | - The submission folders 50 | - The filtered grading worksheet csv 51 | - To assign grades, use `kasm grade` or edit the `grades.toml` file. 52 | - `kasm grade` will infer the team automagically if you're inside 53 | its subfolder 54 | - **IMPORTANT!** Always use the format Moodle expects (especially the decimal separator). 55 | `kasm` doesn't parse your inputs. They are taken at face-value as strings. 56 | - `kasm repack` (in the master directory) packs the feedback into a zip file 57 | (`feedback_$SHEET_$TIMESTAMP.zip`) and grades into `grades_$SHEET_$TIMESTAMP.csv`. 58 | Just upload these at the appropriate place in Moodle. 59 | 60 | **2. Autofetch Workflow** 61 | - Run `kasm setup-fetch` once to 62 | 1. define a course ID (look at the URL in your browser) 63 | 2. save your Moodle Token to your OS's keyring (You can find the token under Moodle > Settings > Tokens > Moodle Mobile Mobile Service) 64 | - Fetch an assignment using `kasm fetch` 65 | - `fetch` will display a list of assignments to select from. 66 | - Grade using `kasm grade` 67 | - Publish your grades automatically using `kasm push` 68 | - Repack your feedback zip using `kasm repack` (in the master directory) 69 | - **Note**: `kasm repack` will **NOT** produce a .csv with Autofetch. You'll need to use `kasm push` to publish grades. 70 | 71 | ### Command line 72 | `kasm` currently has 8 subcommands 73 | 74 | |Subcommand | Explanation | 75 | |-|-| 76 | | help | Get help for `kasm` or for any other subcommand | 77 | | init | Creates a master config file in the current directory | 78 | | unpack | Extracts a moodle zip file, filters a moodle csv for the given group and initializes a slave config in the new directory | 79 | | repack | Repacks the zip for upload to Moodle and constructs a grading worksheet using the slave config | 80 | | grade | Assigns a grade to a group. It is also able to infer the group number if you're currently in its directory. | 81 | | setup-fetch | Saves the course ID to the master config and a Moodle API token to the user's keyring. | 82 | | fetch | Fetches an assignment's submissions (no parameters/interactive) | 83 | | push | Publishes grades (can **only** be used with fetch - not with unpack!!!) | 84 | 85 | 86 | ### Examples 87 | 88 | **Example 1**: Plain usage with CSV and ZIP 89 | ```bash 90 | # cd to your master directory 91 | cd master 92 | 93 | # init kasm (just once for multiple sheets!) 94 | # let's say we have group 01 95 | kasm init -g 01 96 | 97 | # unpack sheet 01 98 | kasm unpack -s 01 -z /path/to/moodle.zip -c /path/to/moodle.csv 99 | 100 | # look at group 01's files 101 | cd 'unpack_01/Übungsgruppe 01 -- Abgabeteam 01' 102 | ... 103 | 104 | # grade group 01 with 16,384 (while inside its folder) 105 | kasm grade 16,384 106 | 107 | # grade group 02 with 8,192 (while inside any folder inside the unpack_XX folder) 108 | kasm grade 8,192 02 109 | 110 | # go back to master folder and repack sheet 01 111 | cd ../../ 112 | kasm repack 01 113 | ``` 114 | 115 | **Example 2**: Autofetch workflow 116 | ```bash 117 | # cd to your master directory 118 | cd master 119 | 120 | # setup the autofetch workflow (needs course ID and moodle token) 121 | kasm fetch-setup 122 | 123 | # fetch some assignment (interactive) 124 | kasm fetch 125 | 126 | # look at group 01's files 127 | cd 'unpack_01/Übungsgruppe 01 -- Abgabeteam 01' 128 | ... 129 | 130 | # grade group 01 with 16,384 (while inside its folder) 131 | kasm grade 16,384 132 | 133 | # publish grades AUTOMAGICALLY 134 | kasm push 135 | 136 | # go back to master and repack (ONLY the ZIP file will get 137 | # repacked if you use autofetch) 138 | cd ../../ 139 | kasm repack 01 140 | ``` 141 | 142 | 143 | ## Plans 144 | 145 | ### Immediate Future 146 | All of these would be better off as script-hooks 147 | - [ ] ~~Recursively extract zips~~ [CANCELED] 148 | - [ ] ~~Prepend all extracted PDFs with e.g. a grading table~~ [CANCELED] 149 | - [ ] ~~Generate said grading table dynamically (LaTeX/Handlebars)~~ [CANCELED] 150 | 151 | ### Soon™ 152 | - [ ] Add script hooks 153 | - [x] ~~Support unpacking groups and then repacking groups~~ 154 | - [ ] Support unpacking individuals and then repacking individuals 155 | - [ ] Support unpacking individuals and then groupping them 156 | 157 | ### Would be cool at some point I guess 158 | - [ ] Hardcode less stuff. Things like target directory names should be handled e.g. by Handlebars 159 | to make everything more easily modifiable for special cases (and if Moodle breaks *again*) 160 | - [x] ~~Automatically download submissions (Moodle API + Token + Page ID)~~ (Only G2G for now) 161 | - [x] ~~Automatically publish grades~~ (Only G2G for now) 162 | - [ ] Automatically upload feedback 163 | 164 | ## Limitations 165 | - Expects the moodle csv header to be *in German*. To change, edit 166 | `src/gradingtable.rs` and recompile. 167 | - No support for nested directiories inside teams' folders when repacking (probably not a problem). 168 | - As discussed [above](#soon), only group -> individual/group mapping is currently implemented. 169 | 170 | ## License 171 | Licensed under EUPL-1.2-or-later. See [license](license). 172 | -------------------------------------------------------------------------------- /src/repack.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info, warn}; 2 | use std::io::Write; 3 | use std::{ 4 | error::Error, 5 | fs::File, 6 | path::PathBuf, 7 | time::{self, UNIX_EPOCH}, 8 | }; 9 | 10 | use crate::config::{Source, Structure}; 11 | use crate::{ 12 | args::RepackDir, 13 | config::{ 14 | Grades, MasterCfg, UNPACK_CSV_FILENAME, UNPACK_GRADES_FILENAME, UNPACK_PATH_FILENAME_BASE, 15 | }, 16 | gradingtable::GradingRecord, 17 | }; 18 | 19 | pub fn repack(master: &MasterCfg, cfg: &RepackDir) -> Result<(), Box> { 20 | let unpacked_path: PathBuf = (UNPACK_PATH_FILENAME_BASE.to_string() + &cfg.sheet_id).into(); 21 | 22 | let packing_time = time::SystemTime::now() 23 | .duration_since(UNIX_EPOCH)? 24 | .as_millis() 25 | .to_string(); 26 | 27 | // Build the repacked zip name 28 | let zip_name: PathBuf = format!("feedback_{}_{}.zip", cfg.sheet_id, &packing_time).into(); 29 | 30 | // Build the new csv name 31 | let grading_csv_name: PathBuf = format!("grades_{}_{}.csv", cfg.sheet_id, &packing_time).into(); 32 | 33 | // Check that we have all that we need 34 | // - Unpacked dir 35 | // - grades.toml 36 | // - .filtered.csv 37 | 38 | if !unpacked_path.is_dir() { 39 | error!("the {:?} is not a directory", unpacked_path); 40 | return Err("".into()); 41 | } 42 | 43 | if !unpacked_path.join(UNPACK_GRADES_FILENAME).is_file() { 44 | error!("grades file could not be found in {:?}", unpacked_path); 45 | return Err("".into()); 46 | } 47 | 48 | // Parse stuff 49 | let grades: Grades = toml::from_str(&std::fs::read_to_string( 50 | unpacked_path.join(UNPACK_GRADES_FILENAME), 51 | )?)?; 52 | let reg = regex::Regex::new(&master.groups_regex)?; 53 | 54 | // Individual files get filtered against this 55 | let internal_reg = regex::Regex::new(match master.repack_filter { 56 | None => "", 57 | Some(ref filter) => filter, 58 | })?; 59 | 60 | if !unpacked_path.join(UNPACK_CSV_FILENAME).is_file() && grades.source == Source::CsvAndZip { 61 | error!( 62 | "filtered csv file could not be found in {:?}", 63 | unpacked_path 64 | ); 65 | return Err("".into()); 66 | } 67 | 68 | let grading_table = 69 | GradingRecord::from_csv(&unpacked_path.join(UNPACK_CSV_FILENAME)).unwrap_or(Vec::new()); 70 | 71 | let csv_writer = if grades.source == Source::CsvAndZip { 72 | Some( 73 | csv::WriterBuilder::new() 74 | .delimiter(b',') 75 | .quote_style(csv::QuoteStyle::Always) 76 | .from_path(grading_csv_name)?, 77 | ) 78 | } else { 79 | None 80 | }; 81 | 82 | // Create the zip file, its writer and config 83 | let zip_file = File::create(zip_name)?; 84 | let mut zip_writer = zip::ZipWriter::new(zip_file); 85 | let zip_options = zip::write::FileOptions::default() 86 | .compression_method(zip::CompressionMethod::Deflated) 87 | .compression_level(Some(9)); 88 | 89 | let repack_fn = match (&master.unpack_structure, &master.repack_structure) { 90 | (Structure::Groups, Structure::Groups) => repack_g2g, 91 | (Structure::Groups, Structure::Individuals) => repack_g2i, 92 | _ => todo!(), 93 | }; 94 | 95 | repack_fn( 96 | &unpacked_path, 97 | &grading_table, 98 | &grades, 99 | ®, 100 | &internal_reg, 101 | &mut zip_writer, 102 | &zip_options, 103 | csv_writer, 104 | )?; 105 | 106 | if grades.source == Source::Autofetch { 107 | warn!("source is autofetch: no .csv was generated!"); 108 | info!("use `kasm push` inside the unpack directory to publish grades"); 109 | } 110 | 111 | Ok(()) 112 | } 113 | 114 | pub fn repack_g2i( 115 | unpacked_path: &PathBuf, 116 | grading_table: &[GradingRecord], 117 | grades: &Grades, 118 | reg: ®ex::Regex, 119 | internal_reg: ®ex::Regex, 120 | zip_writer: &mut zip::ZipWriter, 121 | zip_options: &zip::write::FileOptions, 122 | mut csv_writer: Option>, 123 | ) -> Result<(), Box> { 124 | if csv_writer.is_none() || grades.source == Source::Autofetch { 125 | error!("Group2Individual Repacking is not supported for Autofetch workflows (yet)"); 126 | return Err("".into()); 127 | } 128 | 129 | // Start packing stuff 130 | std::fs::read_dir(unpacked_path)? 131 | .filter_map(|entry| entry.ok()) 132 | // Check that whatever we're packing is a _directory_ 133 | // and it matches the master regex 134 | .filter(|entry| { 135 | entry.path().is_dir() && reg.is_match(entry.file_name().to_str().unwrap_or("")) 136 | }) 137 | .for_each(|filtered| { 138 | info!("filtered: {:?}", filtered.file_name()); 139 | let dir_name = filtered.file_name(); 140 | let group_id = dir_name.to_str().unwrap(); 141 | grades 142 | .collect_students_for_group(grading_table, group_id) 143 | .iter() 144 | .for_each(|studi| { 145 | // Write the student's record to the csv 146 | if let Some(ref mut writer) = csv_writer { 147 | writer.serialize(studi).unwrap(); 148 | } 149 | 150 | // New directory name. Should be something like 151 | // Übungsgruppe AB -- Abgabeteam XY_Name, \ 152 | // Vorname-12345678_assignsubmission_file_ 153 | let dir_new_name: String = format!( 154 | "{group_id}_{s_name}_{s_id}_assignsubmission_file_", 155 | group_id = group_id, 156 | s_name = studi.name, 157 | s_id = studi.internal_id.strip_prefix("Teilnehmer/in").unwrap() 158 | ); 159 | 160 | filtered 161 | .path() 162 | .read_dir() 163 | .unwrap() 164 | .filter_map(|f| f.ok()) 165 | // Match files against the second regex 166 | .filter(|f| internal_reg.is_match(f.file_name().to_str().unwrap())) 167 | .for_each(|f| { 168 | info!("packing {:?}", f); 169 | // Repack each file 170 | let fname = 171 | format!("{}/{}", dir_new_name, f.file_name().to_str().unwrap()); 172 | zip_writer.start_file(fname, *zip_options).unwrap(); 173 | let bytes = std::fs::read(f.path()).unwrap(); 174 | zip_writer.write_all(&bytes).unwrap(); 175 | }); 176 | }); 177 | }); 178 | if let Some(ref mut writer) = csv_writer { 179 | writer.flush()?; 180 | } 181 | zip_writer.flush()?; 182 | 183 | Ok(()) 184 | } 185 | 186 | pub fn repack_g2g( 187 | unpacked_path: &PathBuf, 188 | grading_table: &[GradingRecord], 189 | grades: &Grades, 190 | reg: ®ex::Regex, 191 | internal_reg: ®ex::Regex, 192 | zip_writer: &mut zip::ZipWriter, 193 | zip_options: &zip::write::FileOptions, 194 | mut csv_writer: Option>, 195 | ) -> Result<(), Box> { 196 | // Start packing stuff 197 | std::fs::read_dir(unpacked_path)? 198 | .filter_map(|entry| entry.ok()) 199 | // Check that whatever we're packing is a _directory_ 200 | // and it matches the master regex 201 | .filter(|entry| { 202 | entry.path().is_dir() && reg.is_match(entry.file_name().to_str().unwrap_or("")) 203 | }) 204 | .for_each(|filtered| { 205 | info!("filtered: {:?}", filtered.file_name()); 206 | let dir_name = filtered.file_name(); 207 | let group_name = dir_name.to_str().unwrap(); 208 | 209 | if let Some(ref mut writer) = csv_writer { 210 | grades 211 | .collect_students_for_group(grading_table, group_name) 212 | .iter() 213 | .for_each(|studi| { 214 | // Write the student's record to the csv 215 | writer.serialize(studi).unwrap(); 216 | }); 217 | } 218 | 219 | let group_id = match grades 220 | .map 221 | .iter() 222 | .find(|m| m.target == group_name) 223 | .map(|m| m.internal_id.clone()) 224 | { 225 | Some(Some(internal_id)) => internal_id, 226 | Some(None) => { 227 | error!("Group name ({group_name}) not found. Skipping."); 228 | return; 229 | } 230 | None => { 231 | error!("({group_name}) doens't have an internal ID. Can't repack. Skipping."); 232 | return; 233 | } 234 | }; 235 | 236 | // New directory name. Should be something like 237 | // Übungsgruppe AB -- Abgabeteam XY_12345678_assignsubmission_file 238 | let dir_new_name: String = format!("{group_name}_{group_id}_assignsubmission_file"); 239 | 240 | filtered 241 | .path() 242 | .read_dir() 243 | .unwrap() 244 | .filter_map(|f| f.ok()) 245 | // Match files against the second regex 246 | .filter(|f| internal_reg.is_match(f.file_name().to_str().unwrap())) 247 | .for_each(|f| { 248 | info!("packing {:?}", f); 249 | // Repack each file 250 | let fname = format!("{}/{}", dir_new_name, f.file_name().to_str().unwrap()); 251 | zip_writer.start_file(fname, *zip_options).unwrap(); 252 | let bytes = std::fs::read(f.path()).unwrap(); 253 | zip_writer.write_all(&bytes).unwrap(); 254 | }); 255 | }); 256 | if let Some(ref mut writer) = csv_writer { 257 | writer.flush()?; 258 | } 259 | zip_writer.flush()?; 260 | 261 | Ok(()) 262 | } 263 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | EUPL © the European Union 2007, 2016 3 | 4 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as 5 | defined below) which is provided under the terms of this Licence. Any use of 6 | the Work, other than as authorised under this Licence is prohibited (to the 7 | extent such use is covered by a right of the copyright holder of the Work). 8 | 9 | The Work is provided under the terms of this Licence when the Licensor (as 10 | defined below) has placed the following notice immediately following the 11 | copyright notice for the Work: 12 | 13 | Licensed under the EUPL 14 | 15 | or has expressed by any other means his willingness to license under the EUPL. 16 | 17 | 1. Definitions 18 | 19 | In this Licence, the following terms have the following meaning: 20 | 21 | - ‘The Licence’: this Licence. 22 | 23 | - ‘The Original Work’: the work or software distributed or communicated by the 24 | Licensor under this Licence, available as Source Code and also as Executable 25 | Code as the case may be. 26 | 27 | - ‘Derivative Works’: the works or software that could be created by the 28 | Licensee, based upon the Original Work or modifications thereof. This 29 | Licence does not define the extent of modification or dependence on the 30 | Original Work required in order to classify a work as a Derivative Work; 31 | this extent is determined by copyright law applicable in the country 32 | mentioned in Article 15. 33 | 34 | - ‘The Work’: the Original Work or its Derivative Works. 35 | 36 | - ‘The Source Code’: the human-readable form of the Work which is the most 37 | convenient for people to study and modify. 38 | 39 | - ‘The Executable Code’: any code which has generally been compiled and which 40 | is meant to be interpreted by a computer as a program. 41 | 42 | - ‘The Licensor’: the natural or legal person that distributes or communicates 43 | the Work under the Licence. 44 | 45 | - ‘Contributor(s)’: any natural or legal person who modifies the Work under 46 | the Licence, or otherwise contributes to the creation of a Derivative Work. 47 | 48 | - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of 49 | the Work under the terms of the Licence. 50 | 51 | - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 52 | renting, distributing, communicating, transmitting, or otherwise making 53 | available, online or offline, copies of the Work or providing access to its 54 | essential functionalities at the disposal of any other natural or legal 55 | person. 56 | 57 | 2. Scope of the rights granted by the Licence 58 | 59 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 60 | sublicensable licence to do the following, for the duration of copyright 61 | vested in the Original Work: 62 | 63 | - use the Work in any circumstance and for all usage, 64 | - reproduce the Work, 65 | - modify the Work, and make Derivative Works based upon the Work, 66 | - communicate to the public, including the right to make available or display 67 | the Work or copies thereof to the public and perform publicly, as the case 68 | may be, the Work, 69 | - distribute the Work or copies thereof, 70 | - lend and rent the Work or copies thereof, 71 | - sublicense rights in the Work or copies thereof. 72 | 73 | Those rights can be exercised on any media, supports and formats, whether now 74 | known or later invented, as far as the applicable law permits so. 75 | 76 | In the countries where moral rights apply, the Licensor waives his right to 77 | exercise his moral right to the extent allowed by law in order to make 78 | effective the licence of the economic rights here above listed. 79 | 80 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights 81 | to any patents held by the Licensor, to the extent necessary to make use of 82 | the rights granted on the Work under this Licence. 83 | 84 | 3. Communication of the Source Code 85 | 86 | The Licensor may provide the Work either in its Source Code form, or as 87 | Executable Code. If the Work is provided as Executable Code, the Licensor 88 | provides in addition a machine-readable copy of the Source Code of the Work 89 | along with each copy of the Work that the Licensor distributes or indicates, 90 | in a notice following the copyright notice attached to the Work, a repository 91 | where the Source Code is easily and freely accessible for as long as the 92 | Licensor continues to distribute or communicate the Work. 93 | 94 | 4. Limitations on copyright 95 | 96 | Nothing in this Licence is intended to deprive the Licensee of the benefits 97 | from any exception or limitation to the exclusive rights of the rights owners 98 | in the Work, of the exhaustion of those rights or of other applicable 99 | limitations thereto. 100 | 101 | 5. Obligations of the Licensee 102 | 103 | The grant of the rights mentioned above is subject to some restrictions and 104 | obligations imposed on the Licensee. Those obligations are the following: 105 | 106 | Attribution right: The Licensee shall keep intact all copyright, patent or 107 | trademarks notices and all notices that refer to the Licence and to the 108 | disclaimer of warranties. The Licensee must include a copy of such notices and 109 | a copy of the Licence with every copy of the Work he/she distributes or 110 | communicates. The Licensee must cause any Derivative Work to carry prominent 111 | notices stating that the Work has been modified and the date of modification. 112 | 113 | Copyleft clause: If the Licensee distributes or communicates copies of the 114 | Original Works or Derivative Works, this Distribution or Communication will be 115 | done under the terms of this Licence or of a later version of this Licence 116 | unless the Original Work is expressly distributed only under this version of 117 | the Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 118 | (becoming Licensor) cannot offer or impose any additional terms or conditions 119 | on the Work or Derivative Work that alter or restrict the terms of the 120 | Licence. 121 | 122 | Compatibility clause: If the Licensee Distributes or Communicates Derivative 123 | Works or copies thereof based upon both the Work and another work licensed 124 | under a Compatible Licence, this Distribution or Communication can be done 125 | under the terms of this Compatible Licence. For the sake of this clause, 126 | ‘Compatible Licence’ refers to the licences listed in the appendix attached to 127 | this Licence. Should the Licensee's obligations under the Compatible Licence 128 | conflict with his/her obligations under this Licence, the obligations of the 129 | Compatible Licence shall prevail. 130 | 131 | Provision of Source Code: When distributing or communicating copies of the 132 | Work, the Licensee will provide a machine-readable copy of the Source Code or 133 | indicate a repository where this Source will be easily and freely available 134 | for as long as the Licensee continues to distribute or communicate the Work. 135 | 136 | Legal Protection: This Licence does not grant permission to use the trade 137 | names, trademarks, service marks, or names of the Licensor, except as required 138 | for reasonable and customary use in describing the origin of the Work and 139 | reproducing the content of the copyright notice. 140 | 141 | 6. Chain of Authorship 142 | 143 | The original Licensor warrants that the copyright in the Original Work granted 144 | hereunder is owned by him/her or licensed to him/her and that he/she has the 145 | power and authority to grant the Licence. 146 | 147 | Each Contributor warrants that the copyright in the modifications he/she 148 | brings to the Work are owned by him/her or licensed to him/her and that he/she 149 | has the power and authority to grant the Licence. 150 | 151 | Each time You accept the Licence, the original Licensor and subsequent 152 | Contributors grant You a licence to their contributions to the Work, under the 153 | terms of this Licence. 154 | 155 | 7. Disclaimer of Warranty 156 | 157 | The Work is a work in progress, which is continuously improved by numerous 158 | Contributors. It is not a finished work and may therefore contain defects or 159 | ‘bugs’ inherent to this type of development. 160 | 161 | For the above reason, the Work is provided under the Licence on an ‘as is’ 162 | basis and without warranties of any kind concerning the Work, including 163 | without limitation merchantability, fitness for a particular purpose, absence 164 | of defects or errors, accuracy, non-infringement of intellectual property 165 | rights other than copyright as stated in Article 6 of this Licence. 166 | 167 | This disclaimer of warranty is an essential part of the Licence and a 168 | condition for the grant of any rights to the Work. 169 | 170 | 8. Disclaimer of Liability 171 | 172 | Except in the cases of wilful misconduct or damages directly caused to natural 173 | persons, the Licensor will in no event be liable for any direct or indirect, 174 | material or moral, damages of any kind, arising out of the Licence or of the 175 | use of the Work, including without limitation, damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, loss of data or any commercial 177 | damage, even if the Licensor has been advised of the possibility of such 178 | damage. However, the Licensor will be liable under statutory product liability 179 | laws as far such laws apply to the Work. 180 | 181 | 9. Additional agreements 182 | 183 | While distributing the Work, You may choose to conclude an additional 184 | agreement, defining obligations or services consistent with this Licence. 185 | However, if accepting obligations, You may act only on your own behalf and on 186 | your sole responsibility, not on behalf of the original Licensor or any other 187 | Contributor, and only if You agree to indemnify, defend, and hold each 188 | Contributor harmless for any liability incurred by, or claims asserted against 189 | such Contributor by the fact You have accepted any warranty or additional 190 | liability. 191 | 192 | 10. Acceptance of the Licence 193 | 194 | The provisions of this Licence can be accepted by clicking on an icon ‘I 195 | agree’ placed under the bottom of a window displaying the text of this Licence 196 | or by affirming consent in any other similar way, in accordance with the rules 197 | of applicable law. Clicking on that icon indicates your clear and irrevocable 198 | acceptance of this Licence and all of its terms and conditions. 199 | 200 | Similarly, you irrevocably accept this Licence and all of its terms and 201 | conditions by exercising any rights granted to You by Article 2 of this 202 | Licence, such as the use of the Work, the creation by You of a Derivative Work 203 | or the Distribution or Communication by You of the Work or copies thereof. 204 | 205 | 11. Information to the public 206 | 207 | In case of any Distribution or Communication of the Work by means of 208 | electronic communication by You (for example, by offering to download the Work 209 | from a remote location) the distribution channel or media (for example, a 210 | website) must at least provide to the public the information requested by the 211 | applicable law regarding the Licensor, the Licence and the way it may be 212 | accessible, concluded, stored and reproduced by the Licensee. 213 | 214 | 12. Termination of the Licence 215 | 216 | The Licence and the rights granted hereunder will terminate automatically upon 217 | any breach by the Licensee of the terms of the Licence. 218 | 219 | Such a termination will not terminate the licences of any person who has 220 | received the Work from the Licensee under the Licence, provided such persons 221 | remain in full compliance with the Licence. 222 | 223 | 13. Miscellaneous 224 | 225 | Without prejudice of Article 9 above, the Licence represents the complete 226 | agreement between the Parties as to the Work. 227 | 228 | If any provision of the Licence is invalid or unenforceable under applicable 229 | law, this will not affect the validity or enforceability of the Licence as a 230 | whole. Such provision will be construed or reformed so as necessary to make it 231 | valid and enforceable. 232 | 233 | The European Commission may publish other linguistic versions or new versions 234 | of this Licence or updated versions of the Appendix, so far this is required 235 | and reasonable, without reducing the scope of the rights granted by the 236 | Licence. New versions of the Licence will be published with a unique version 237 | number. 238 | 239 | All linguistic versions of this Licence, approved by the European Commission, 240 | have identical value. Parties can take advantage of the linguistic version of 241 | their choice. 242 | 243 | 14. Jurisdiction 244 | 245 | Without prejudice to specific agreement between parties, 246 | 247 | - any litigation resulting from the interpretation of this License, arising 248 | between the European Union institutions, bodies, offices or agencies, as a 249 | Licensor, and any Licensee, will be subject to the jurisdiction of the Court 250 | of Justice of the European Union, as laid down in article 272 of the Treaty 251 | on the Functioning of the European Union, 252 | 253 | - any litigation arising between other parties and resulting from the 254 | interpretation of this License, will be subject to the exclusive 255 | jurisdiction of the competent court where the Licensor resides or conducts 256 | its primary business. 257 | 258 | 15. Applicable Law 259 | 260 | Without prejudice to specific agreement between parties, 261 | 262 | - this Licence shall be governed by the law of the European Union Member State 263 | where the Licensor has his seat, resides or has his registered office, 264 | 265 | - this licence shall be governed by Belgian law if the Licensor has no seat, 266 | residence or registered office inside a European Union Member State. 267 | 268 | Appendix 269 | 270 | ‘Compatible Licences’ according to Article 5 EUPL are: 271 | 272 | - GNU General Public License (GPL) v. 2, v. 3 273 | - GNU Affero General Public License (AGPL) v. 3 274 | - Open Software License (OSL) v. 2.1, v. 3.0 275 | - Eclipse Public License (EPL) v. 1.0 276 | - CeCILL v. 2.0, v. 2.1 277 | - Mozilla Public Licence (MPL) v. 2 278 | - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 279 | - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 280 | works other than software 281 | - European Union Public Licence (EUPL) v. 1.1, v. 1.2 282 | - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 283 | Reciprocity (LiLiQ-R+). 284 | 285 | The European Commission may update this Appendix to later versions of the 286 | above licences without producing a new version of the EUPL, as long as they 287 | provide the rights granted in Article 2 of this Licence and protect the 288 | covered Source Code from exclusive appropriation. 289 | 290 | All other changes or additions to this Appendix require the production of a 291 | new EUPL version. 292 | -------------------------------------------------------------------------------- /src/fetch.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | use std::{collections::HashMap, error::Error, path::PathBuf, time::Duration}; 3 | 4 | use crate::config::{Grade, Grades, MasterCfg, UNPACK_GRADES_FILENAME, UNPACK_PATH_FILENAME_BASE}; 5 | use log::{error, info, warn}; 6 | 7 | const KEYRING_SERVICE_NAME: &str = "kasm-moodle-token"; 8 | static MOODLE_REST_URL: &str = "https://moodle.rwth-aachen.de/webservice/rest/server.php"; 9 | 10 | pub fn setup(master: &MasterCfg) -> core::result::Result<(), Box> { 11 | let overwrite_course; 12 | 13 | let mut new_master = master.clone(); 14 | let user = whoami::username(); 15 | 16 | if let Some(course) = &master.moodle_course_id { 17 | info!("the saved course id is {}.", course); 18 | print!("overwrite? (y/N) > "); 19 | overwrite_course = inquire::Confirm::new("overwrite? (y/n) > ") 20 | .prompt_skippable()? 21 | .unwrap_or(false); 22 | } else { 23 | overwrite_course = true; 24 | } 25 | 26 | if overwrite_course { 27 | new_master.moodle_course_id = inquire::Text::new("Course ID > ").prompt()?.into(); 28 | } 29 | 30 | info!( 31 | "using course id = {{{}}}", 32 | new_master.moodle_course_id.as_ref().unwrap() 33 | ); 34 | 35 | let entry = keyring::Entry::new(KEYRING_SERVICE_NAME, &user)?; 36 | let overwrite_token = match entry.get_password() { 37 | Err(keyring::Error::NoEntry) => true, 38 | Err(keyring::Error::Ambiguous(a)) => { 39 | error!("{} is ambiguous in your keyring", KEYRING_SERVICE_NAME); 40 | return Err(keyring::Error::Ambiguous(a).into()); 41 | } 42 | Err(e) => return Err(e.into()), 43 | Ok(_) => { 44 | info!( 45 | "your keyring already contains an entry for {}", 46 | KEYRING_SERVICE_NAME 47 | ); 48 | inquire::Confirm::new("overwrite? (y/n) > ") 49 | .prompt_skippable()? 50 | .unwrap_or(false) 51 | } 52 | }; 53 | 54 | if overwrite_token { 55 | let token = inquire::Password::new("Moodle Token (won't be echoed): ") 56 | .without_confirmation() 57 | .prompt()?; 58 | info!( 59 | "saving token to keyring ({}, {})", 60 | KEYRING_SERVICE_NAME, user 61 | ); 62 | entry.set_password(&token)?; 63 | } 64 | 65 | std::fs::write( 66 | new_master.location.clone(), 67 | toml::to_string_pretty(&new_master)?, 68 | )?; 69 | Ok(()) 70 | } 71 | 72 | #[derive(Debug, Clone)] 73 | pub struct SubmissionFileMap { 74 | pub dl_path: PathBuf, 75 | pub dl_url: String, 76 | pub group_id: String, 77 | pub group_name: String, 78 | } 79 | 80 | pub struct MoodleFetcher { 81 | pub course_id: String, 82 | pub config: MasterCfg, 83 | token: String, 84 | } 85 | 86 | impl MoodleFetcher { 87 | pub fn new(config: &MasterCfg) -> MoodleFetcher { 88 | let entry = 89 | keyring::Entry::new(KEYRING_SERVICE_NAME, &whoami::username()).unwrap_or_else(|e| { 90 | error!("{}", e); 91 | error!("run setup-fetch first!"); 92 | panic!(); 93 | }); 94 | 95 | MoodleFetcher { 96 | config: config.clone(), 97 | course_id: config 98 | .moodle_course_id 99 | .as_ref() 100 | .unwrap_or_else(|| { 101 | error!("run fetch-setup first!"); 102 | panic!() 103 | }) 104 | .clone(), 105 | token: entry.get_password().unwrap(), 106 | } 107 | } 108 | 109 | pub fn fetch_directory(&self) -> Result, Box> { 110 | let resp = reqwest::blocking::Client::new() 111 | .get(MOODLE_REST_URL) 112 | .query(&[ 113 | ("moodlewsrestformat", "json"), 114 | ("wsfunction", "mod_assign_get_assignments"), 115 | ("wstoken", &self.token), 116 | ("courseids[0]", &self.course_id), 117 | ]) 118 | .send()?; 119 | 120 | let data: serde_json::Value = serde_json::de::from_str(&resp.text()?)?; 121 | Ok(data 122 | .get("courses") 123 | .unwrap() 124 | .get(0) 125 | .unwrap() 126 | .get("assignments") 127 | .unwrap() 128 | .as_array() 129 | .unwrap() 130 | .iter() 131 | .map(|assignment| { 132 | ( 133 | assignment 134 | .get("name") 135 | .unwrap() 136 | .as_str() 137 | .unwrap() 138 | .to_string(), 139 | assignment.get("id").unwrap().to_string(), 140 | ) 141 | }) 142 | .collect::>()) 143 | } 144 | 145 | pub fn get_submissions_list( 146 | &self, 147 | assignment_id: &String, 148 | ) -> Result>, Box> { 149 | info!("getting submission list"); 150 | warn!("this is going to take an eternity in big course pages"); 151 | warn!("compare how slow moodle is when you click on 'view all submissions'"); 152 | warn!("go brew a coffee or touch grass or something"); 153 | let resp = reqwest::blocking::Client::new() 154 | .get(MOODLE_REST_URL) 155 | .query(&[ 156 | ("moodlewsrestformat", "json"), 157 | ("wsfunction", "mod_assign_get_submissions"), 158 | ("wstoken", &self.token), 159 | ("assignmentids[0]", assignment_id), 160 | ]) 161 | .timeout(Duration::new(900, 0)) // 15 Min Timeout because Moodle is _fast_ 162 | .send()?; 163 | 164 | let rt = &resp.text()?; 165 | //let rt = include_str!("../target/submissions.json"); 166 | let parsed: Value = serde_json::from_str(rt)?; 167 | 168 | let gid_plug_arrs: HashMap> = parsed 169 | .get("assignments") 170 | .unwrap() 171 | .get(0) 172 | .unwrap() 173 | .get("submissions") 174 | .unwrap() 175 | .as_array() 176 | .unwrap() 177 | .iter() 178 | .filter_map(|submission| { 179 | (submission.get("groupid")?, submission.get("plugins")?).into() 180 | }) 181 | .map(|(a, b)| { 182 | ( 183 | a.to_string(), 184 | b.as_array() 185 | .unwrap() 186 | .iter() 187 | .filter_map(|plug| plug.get("fileareas")?.as_array()) 188 | .flatten() 189 | .filter(|fa| fa.get("area").unwrap() == "submission_files") 190 | .filter_map(|fa| fa.get("files")) 191 | .filter_map(|ff| ff.as_array()) 192 | .flatten() 193 | .cloned() 194 | .collect(), 195 | ) 196 | }) 197 | .collect(); 198 | 199 | Ok(gid_plug_arrs) 200 | } 201 | 202 | pub fn get_group_mappings( 203 | &self, 204 | assignment_id: &String, 205 | ) -> Result<(HashMap, HashMap>), Box> { 206 | info!("fetching participants list"); 207 | let resp = reqwest::blocking::Client::new() 208 | .get(MOODLE_REST_URL) 209 | .query(&[ 210 | ("moodlewsrestformat", "json"), 211 | ("wsfunction", "mod_assign_list_participants"), 212 | ("wstoken", &self.token), 213 | ("assignid", assignment_id), 214 | ("groupid", "0"), 215 | ("onlyids", "1"), 216 | ("filter", ""), 217 | ]) 218 | .timeout(Duration::new(900, 0)) 219 | .send()?; 220 | let rt = resp.text()?; 221 | 222 | //let rt = include_str!("../target/participants.json"); 223 | let parsed: Value = serde_json::from_str(&rt)?; 224 | let mut group_members_mappings: HashMap> = HashMap::new(); 225 | 226 | let mut groups: HashMap = HashMap::new(); 227 | parsed 228 | .as_array() 229 | .unwrap() 230 | .iter() 231 | .filter_map(|part| { 232 | ( 233 | part.get("id")?, 234 | part.get("groupname")?, 235 | part.get("groupid")?, 236 | part.get("submissionstatus")?, 237 | ) 238 | .into() 239 | }) 240 | .filter(|(_, _, _, status)| status.as_str().unwrap() == "submitted") 241 | .for_each(|(userid, gname, gid, _)| { 242 | group_members_mappings 243 | .entry(gid.to_string()) 244 | .or_insert_with(Vec::new) 245 | .push(userid.to_string()); 246 | groups 247 | .entry(gid.to_string()) 248 | .or_insert_with(|| gname.as_str().unwrap().into()); 249 | }); 250 | 251 | Ok((groups, group_members_mappings)) 252 | } 253 | 254 | pub fn interactive_dl(&mut self) -> Result<(), Box> { 255 | let assignments = self.fetch_directory()?; 256 | let mut prompt_revord: Vec<&String> = assignments.keys().collect(); 257 | prompt_revord.sort_unstable(); 258 | prompt_revord.reverse(); 259 | let selected = 260 | inquire::Select::new("Select an assignment to download", prompt_revord).prompt()?; 261 | 262 | let reg = regex::Regex::new(&self.config.groups_regex)?; 263 | let dl_id = assignments.get(selected).unwrap(); 264 | 265 | let participants = self.get_group_mappings(dl_id)?; 266 | let submissions = self.get_submissions_list(dl_id)?; 267 | 268 | let nr_regex = regex::Regex::new(r"(\d?\d)")?; 269 | 270 | let base_path = PathBuf::from( 271 | UNPACK_PATH_FILENAME_BASE.to_string() 272 | + nr_regex 273 | .captures(selected.as_str()) 274 | .unwrap() 275 | .get(1) 276 | .unwrap() 277 | .into(), 278 | ); 279 | info!("Sel {:?}", base_path); 280 | 281 | let filtered_participants: HashMap<&String, &String> = participants 282 | .0 283 | .iter() 284 | .filter_map(|(k, v)| { 285 | if reg.captures(v)?.get(1)?.as_str() == self.config.group { 286 | Some((k, v)) 287 | } else { 288 | None 289 | } 290 | }) 291 | .collect(); 292 | 293 | let sheet_id = selected.split(' ').last().unwrap().to_string(); 294 | let loc = format!("{}{}", UNPACK_PATH_FILENAME_BASE, sheet_id); 295 | let mut config = Grades { 296 | location: loc.into(), 297 | sheet_id: selected.split(' ').last().unwrap().to_string(), 298 | map: Default::default(), 299 | source: crate::config::Source::Autofetch, 300 | assign_id: Some(dl_id.to_owned()), 301 | }; 302 | self.gen_grading_files(&mut config, &filtered_participants, &participants.1)?; 303 | 304 | let filtered_files: Vec = filtered_participants 305 | .iter() 306 | .filter(|(_, v)| reg.captures(v).unwrap().get(1).unwrap().as_str() == self.config.group) 307 | .flat_map(|(gid, gname)| { 308 | let group_path = base_path.join(gname); 309 | submissions 310 | .get(gid.as_str()) 311 | .unwrap() 312 | .iter() 313 | .filter_map(move |file| { 314 | SubmissionFileMap { 315 | dl_url: file.get("fileurl")?.as_str()?.to_string(), 316 | dl_path: group_path.join(file.get("filename")?.as_str()?), 317 | group_id: gid.to_string(), 318 | group_name: gname.to_string(), 319 | } 320 | .into() 321 | }) 322 | }) 323 | .collect(); 324 | 325 | info!("downloading {} file(s)", filtered_files.len()); 326 | for file in &filtered_files { 327 | info!("downloading submission of {{{}}}", file.group_name); 328 | std::fs::create_dir_all(file.dl_path.parent().unwrap())?; 329 | let resp = reqwest::blocking::Client::new() 330 | .get(&file.dl_url) 331 | .query(&[("token", self.token.as_str())]) 332 | .timeout(Duration::new(900, 0)) 333 | .send()?; 334 | 335 | std::fs::write(&file.dl_path, resp.bytes()?)?; 336 | } 337 | 338 | info!("done"); 339 | warn!("note: repacking autofetched files is not implemented yet!"); 340 | 341 | Ok(()) 342 | } 343 | fn gen_grading_files( 344 | &self, 345 | conf: &mut Grades, 346 | groups: &HashMap<&String, &String>, 347 | group_user_mappings: &HashMap>, 348 | ) -> Result<(), Box> { 349 | let mut grades_arr: Vec = Vec::new(); 350 | let mut seen: Vec = Vec::new(); 351 | 352 | groups.iter().for_each(|(&gid, &gname)| { 353 | grades_arr.push(Grade { 354 | grade: "0".to_string(), 355 | internal_id: Some(gid.to_owned()), 356 | members: group_user_mappings.get(gid).cloned(), 357 | target: gname.to_owned(), 358 | }); 359 | seen.push(gid.to_owned()); 360 | }); 361 | 362 | info!("saw {} discreet groups", seen.len()); 363 | 364 | let grades_toml_path = conf.location.join(UNPACK_GRADES_FILENAME); 365 | 366 | info!("writing {:#?}", grades_toml_path); 367 | std::fs::create_dir_all(&conf.location)?; 368 | std::fs::write( 369 | grades_toml_path.clone(), 370 | toml::to_string_pretty(&Grades { 371 | location: grades_toml_path, 372 | map: grades_arr, 373 | sheet_id: conf.sheet_id.to_owned(), 374 | source: conf.source.to_owned(), 375 | assign_id: conf.assign_id.to_owned(), 376 | })?, 377 | )?; 378 | 379 | Ok(()) 380 | } 381 | 382 | fn set_grade_for( 383 | &self, 384 | assignid: String, 385 | userid: String, 386 | grade: String, 387 | dry_run: bool, 388 | ) -> Result<(), Box> { 389 | if dry_run { 390 | let userdata_req = reqwest::blocking::Client::new() 391 | .get(MOODLE_REST_URL) 392 | .query(&[ 393 | ("moodlewsrestformat", "json"), 394 | ("wsfunction", "mod_assign_get_participant"), 395 | ("wstoken", self.token.as_str()), 396 | ("assignid", assignid.as_str()), 397 | ("userid", userid.as_str()), 398 | ]) 399 | .timeout(Duration::new(900, 0)) 400 | .send()?; 401 | 402 | let userdata: serde_json::Value = serde_json::from_reader(userdata_req)?; 403 | let uname = userdata.get("fullname").unwrap().as_str().unwrap(); 404 | let gname = userdata.get("groupname").unwrap().as_str().unwrap(); 405 | 406 | info!("dry-run: would set {grade} for ({uname}) AND Group ({gname})"); 407 | return Ok(()); 408 | } 409 | 410 | let adj_grade = grade.replace(',', "."); 411 | 412 | info!("Grading {userid} with {grade}"); 413 | let req = reqwest::blocking::Client::new() 414 | .get(MOODLE_REST_URL) 415 | .query(&[ 416 | ("moodlewsrestformat", "json"), 417 | ("wsfunction", "mod_assign_save_grade"), 418 | ("wstoken", self.token.as_str()), 419 | // Moodle IDs 420 | ("assignmentid", assignid.as_str()), 421 | ("userid", userid.as_str()), 422 | // Grade latest attempt 423 | ("attemptnumber", "-1"), 424 | // Set to graded 425 | ("workflowstate", "graded"), 426 | // Do not allow another attempt 427 | ("addattempt", "0"), 428 | // The grade itself 429 | ("grade", adj_grade.as_str()), 430 | // Apply to whole group 431 | ("applytoall", "1"), 432 | // Text Feedback (not implemented yet but Moodle needs it to be 433 | // here) 434 | ("plugindata[assignfeedbackcomments_editor][text]", ""), 435 | ("plugindata[assignfeedbackcomments_editor][format]", "4"), 436 | ]) 437 | .timeout(Duration::new(900, 0)) 438 | .send()?; 439 | 440 | log::debug!("{:#?}", req.url()); 441 | log::debug!("{}", req.text()?); 442 | 443 | Ok(()) 444 | } 445 | 446 | pub fn push_grades(&mut self, grades: &Grades, dry_run: bool) -> Result<(), Box> { 447 | let assign_id = grades 448 | .assign_id 449 | .clone() 450 | .expect("Moodle Assignment ID in grades.toml"); 451 | 452 | grades.map.iter().for_each(|record| { 453 | let members = record.members.clone().unwrap(); 454 | self.set_grade_for( 455 | assign_id.to_owned(), 456 | members.get(0).unwrap().to_owned(), 457 | record.grade.to_owned(), 458 | dry_run, 459 | ) 460 | .expect("success setting grade"); 461 | }); 462 | 463 | Ok(()) 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aes" 22 | version = "0.7.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" 25 | dependencies = [ 26 | "cfg-if", 27 | "cipher", 28 | "cpufeatures", 29 | "opaque-debug", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "anstream" 43 | version = "0.6.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" 46 | dependencies = [ 47 | "anstyle", 48 | "anstyle-parse", 49 | "anstyle-query", 50 | "anstyle-wincon", 51 | "colorchoice", 52 | "utf8parse", 53 | ] 54 | 55 | [[package]] 56 | name = "anstyle" 57 | version = "1.0.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 60 | 61 | [[package]] 62 | name = "anstyle-parse" 63 | version = "0.2.2" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 66 | dependencies = [ 67 | "utf8parse", 68 | ] 69 | 70 | [[package]] 71 | name = "anstyle-query" 72 | version = "1.0.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 75 | dependencies = [ 76 | "windows-sys", 77 | ] 78 | 79 | [[package]] 80 | name = "anstyle-wincon" 81 | version = "3.0.1" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 84 | dependencies = [ 85 | "anstyle", 86 | "windows-sys", 87 | ] 88 | 89 | [[package]] 90 | name = "async-broadcast" 91 | version = "0.5.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" 94 | dependencies = [ 95 | "event-listener 2.5.3", 96 | "futures-core", 97 | ] 98 | 99 | [[package]] 100 | name = "async-channel" 101 | version = "1.9.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 104 | dependencies = [ 105 | "concurrent-queue", 106 | "event-listener 2.5.3", 107 | "futures-core", 108 | ] 109 | 110 | [[package]] 111 | name = "async-executor" 112 | version = "1.6.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" 115 | dependencies = [ 116 | "async-lock", 117 | "async-task", 118 | "concurrent-queue", 119 | "fastrand 2.0.1", 120 | "futures-lite", 121 | "slab", 122 | ] 123 | 124 | [[package]] 125 | name = "async-fs" 126 | version = "1.6.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" 129 | dependencies = [ 130 | "async-lock", 131 | "autocfg", 132 | "blocking", 133 | "futures-lite", 134 | ] 135 | 136 | [[package]] 137 | name = "async-io" 138 | version = "1.13.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" 141 | dependencies = [ 142 | "async-lock", 143 | "autocfg", 144 | "cfg-if", 145 | "concurrent-queue", 146 | "futures-lite", 147 | "log", 148 | "parking", 149 | "polling 2.8.0", 150 | "rustix 0.37.27", 151 | "slab", 152 | "socket2 0.4.10", 153 | "waker-fn", 154 | ] 155 | 156 | [[package]] 157 | name = "async-io" 158 | version = "2.1.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "10da8f3146014722c89e7859e1d7bb97873125d7346d10ca642ffab794355828" 161 | dependencies = [ 162 | "async-lock", 163 | "cfg-if", 164 | "concurrent-queue", 165 | "futures-io", 166 | "futures-lite", 167 | "parking", 168 | "polling 3.3.0", 169 | "rustix 0.38.21", 170 | "slab", 171 | "tracing", 172 | "waker-fn", 173 | "windows-sys", 174 | ] 175 | 176 | [[package]] 177 | name = "async-lock" 178 | version = "2.8.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" 181 | dependencies = [ 182 | "event-listener 2.5.3", 183 | ] 184 | 185 | [[package]] 186 | name = "async-process" 187 | version = "1.8.1" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" 190 | dependencies = [ 191 | "async-io 1.13.0", 192 | "async-lock", 193 | "async-signal", 194 | "blocking", 195 | "cfg-if", 196 | "event-listener 3.0.1", 197 | "futures-lite", 198 | "rustix 0.38.21", 199 | "windows-sys", 200 | ] 201 | 202 | [[package]] 203 | name = "async-recursion" 204 | version = "1.0.5" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" 207 | dependencies = [ 208 | "proc-macro2", 209 | "quote", 210 | "syn 2.0.38", 211 | ] 212 | 213 | [[package]] 214 | name = "async-signal" 215 | version = "0.2.5" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" 218 | dependencies = [ 219 | "async-io 2.1.0", 220 | "async-lock", 221 | "atomic-waker", 222 | "cfg-if", 223 | "futures-core", 224 | "futures-io", 225 | "rustix 0.38.21", 226 | "signal-hook-registry", 227 | "slab", 228 | "windows-sys", 229 | ] 230 | 231 | [[package]] 232 | name = "async-task" 233 | version = "4.5.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" 236 | 237 | [[package]] 238 | name = "async-trait" 239 | version = "0.1.74" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" 242 | dependencies = [ 243 | "proc-macro2", 244 | "quote", 245 | "syn 2.0.38", 246 | ] 247 | 248 | [[package]] 249 | name = "atomic-waker" 250 | version = "1.1.2" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 253 | 254 | [[package]] 255 | name = "autocfg" 256 | version = "1.1.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 259 | 260 | [[package]] 261 | name = "backtrace" 262 | version = "0.3.69" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 265 | dependencies = [ 266 | "addr2line", 267 | "cc", 268 | "cfg-if", 269 | "libc", 270 | "miniz_oxide", 271 | "object", 272 | "rustc-demangle", 273 | ] 274 | 275 | [[package]] 276 | name = "base64" 277 | version = "0.21.5" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" 280 | 281 | [[package]] 282 | name = "bitflags" 283 | version = "1.3.2" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 286 | 287 | [[package]] 288 | name = "bitflags" 289 | version = "2.4.1" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 292 | 293 | [[package]] 294 | name = "block-buffer" 295 | version = "0.10.4" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 298 | dependencies = [ 299 | "generic-array", 300 | ] 301 | 302 | [[package]] 303 | name = "block-modes" 304 | version = "0.8.1" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" 307 | dependencies = [ 308 | "block-padding", 309 | "cipher", 310 | ] 311 | 312 | [[package]] 313 | name = "block-padding" 314 | version = "0.2.1" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" 317 | 318 | [[package]] 319 | name = "blocking" 320 | version = "1.4.1" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" 323 | dependencies = [ 324 | "async-channel", 325 | "async-lock", 326 | "async-task", 327 | "fastrand 2.0.1", 328 | "futures-io", 329 | "futures-lite", 330 | "piper", 331 | "tracing", 332 | ] 333 | 334 | [[package]] 335 | name = "bumpalo" 336 | version = "3.14.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 339 | 340 | [[package]] 341 | name = "byteorder" 342 | version = "1.5.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 345 | 346 | [[package]] 347 | name = "bytes" 348 | version = "1.5.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" 351 | 352 | [[package]] 353 | name = "cc" 354 | version = "1.0.83" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 357 | dependencies = [ 358 | "libc", 359 | ] 360 | 361 | [[package]] 362 | name = "cfg-if" 363 | version = "1.0.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 366 | 367 | [[package]] 368 | name = "cipher" 369 | version = "0.3.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" 372 | dependencies = [ 373 | "generic-array", 374 | ] 375 | 376 | [[package]] 377 | name = "clap" 378 | version = "4.4.7" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" 381 | dependencies = [ 382 | "clap_builder", 383 | "clap_derive", 384 | ] 385 | 386 | [[package]] 387 | name = "clap_builder" 388 | version = "4.4.7" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" 391 | dependencies = [ 392 | "anstream", 393 | "anstyle", 394 | "clap_lex", 395 | "strsim", 396 | ] 397 | 398 | [[package]] 399 | name = "clap_derive" 400 | version = "4.4.7" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" 403 | dependencies = [ 404 | "heck", 405 | "proc-macro2", 406 | "quote", 407 | "syn 2.0.38", 408 | ] 409 | 410 | [[package]] 411 | name = "clap_lex" 412 | version = "0.6.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 415 | 416 | [[package]] 417 | name = "colorchoice" 418 | version = "1.0.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 421 | 422 | [[package]] 423 | name = "concurrent-queue" 424 | version = "2.3.0" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" 427 | dependencies = [ 428 | "crossbeam-utils", 429 | ] 430 | 431 | [[package]] 432 | name = "core-foundation" 433 | version = "0.9.3" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 436 | dependencies = [ 437 | "core-foundation-sys", 438 | "libc", 439 | ] 440 | 441 | [[package]] 442 | name = "core-foundation-sys" 443 | version = "0.8.4" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 446 | 447 | [[package]] 448 | name = "cpufeatures" 449 | version = "0.2.11" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" 452 | dependencies = [ 453 | "libc", 454 | ] 455 | 456 | [[package]] 457 | name = "crc32fast" 458 | version = "1.3.2" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 461 | dependencies = [ 462 | "cfg-if", 463 | ] 464 | 465 | [[package]] 466 | name = "crossbeam-utils" 467 | version = "0.8.16" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" 470 | dependencies = [ 471 | "cfg-if", 472 | ] 473 | 474 | [[package]] 475 | name = "crossterm" 476 | version = "0.25.0" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" 479 | dependencies = [ 480 | "bitflags 1.3.2", 481 | "crossterm_winapi", 482 | "libc", 483 | "mio", 484 | "parking_lot", 485 | "signal-hook", 486 | "signal-hook-mio", 487 | "winapi", 488 | ] 489 | 490 | [[package]] 491 | name = "crossterm_winapi" 492 | version = "0.9.1" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 495 | dependencies = [ 496 | "winapi", 497 | ] 498 | 499 | [[package]] 500 | name = "crypto-common" 501 | version = "0.1.6" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 504 | dependencies = [ 505 | "generic-array", 506 | "typenum", 507 | ] 508 | 509 | [[package]] 510 | name = "csv" 511 | version = "1.3.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" 514 | dependencies = [ 515 | "csv-core", 516 | "itoa", 517 | "ryu", 518 | "serde", 519 | ] 520 | 521 | [[package]] 522 | name = "csv-core" 523 | version = "0.1.11" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" 526 | dependencies = [ 527 | "memchr", 528 | ] 529 | 530 | [[package]] 531 | name = "deranged" 532 | version = "0.3.9" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" 535 | dependencies = [ 536 | "powerfmt", 537 | ] 538 | 539 | [[package]] 540 | name = "derivative" 541 | version = "2.2.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 544 | dependencies = [ 545 | "proc-macro2", 546 | "quote", 547 | "syn 1.0.109", 548 | ] 549 | 550 | [[package]] 551 | name = "digest" 552 | version = "0.10.7" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 555 | dependencies = [ 556 | "block-buffer", 557 | "crypto-common", 558 | "subtle", 559 | ] 560 | 561 | [[package]] 562 | name = "dyn-clone" 563 | version = "1.0.14" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" 566 | 567 | [[package]] 568 | name = "encoding_rs" 569 | version = "0.8.33" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" 572 | dependencies = [ 573 | "cfg-if", 574 | ] 575 | 576 | [[package]] 577 | name = "enumflags2" 578 | version = "0.7.8" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" 581 | dependencies = [ 582 | "enumflags2_derive", 583 | "serde", 584 | ] 585 | 586 | [[package]] 587 | name = "enumflags2_derive" 588 | version = "0.7.8" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" 591 | dependencies = [ 592 | "proc-macro2", 593 | "quote", 594 | "syn 2.0.38", 595 | ] 596 | 597 | [[package]] 598 | name = "env_logger" 599 | version = "0.10.0" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" 602 | dependencies = [ 603 | "humantime", 604 | "is-terminal", 605 | "log", 606 | "regex", 607 | "termcolor", 608 | ] 609 | 610 | [[package]] 611 | name = "equivalent" 612 | version = "1.0.1" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 615 | 616 | [[package]] 617 | name = "errno" 618 | version = "0.3.5" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" 621 | dependencies = [ 622 | "libc", 623 | "windows-sys", 624 | ] 625 | 626 | [[package]] 627 | name = "event-listener" 628 | version = "2.5.3" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 631 | 632 | [[package]] 633 | name = "event-listener" 634 | version = "3.0.1" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1" 637 | dependencies = [ 638 | "concurrent-queue", 639 | "parking", 640 | "pin-project-lite", 641 | ] 642 | 643 | [[package]] 644 | name = "fastrand" 645 | version = "1.9.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 648 | dependencies = [ 649 | "instant", 650 | ] 651 | 652 | [[package]] 653 | name = "fastrand" 654 | version = "2.0.1" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 657 | 658 | [[package]] 659 | name = "flate2" 660 | version = "1.0.28" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 663 | dependencies = [ 664 | "crc32fast", 665 | "miniz_oxide", 666 | ] 667 | 668 | [[package]] 669 | name = "fnv" 670 | version = "1.0.7" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 673 | 674 | [[package]] 675 | name = "foreign-types" 676 | version = "0.3.2" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 679 | dependencies = [ 680 | "foreign-types-shared", 681 | ] 682 | 683 | [[package]] 684 | name = "foreign-types-shared" 685 | version = "0.1.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 688 | 689 | [[package]] 690 | name = "form_urlencoded" 691 | version = "1.2.0" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" 694 | dependencies = [ 695 | "percent-encoding", 696 | ] 697 | 698 | [[package]] 699 | name = "futures-channel" 700 | version = "0.3.29" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" 703 | dependencies = [ 704 | "futures-core", 705 | ] 706 | 707 | [[package]] 708 | name = "futures-core" 709 | version = "0.3.29" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" 712 | 713 | [[package]] 714 | name = "futures-io" 715 | version = "0.3.29" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" 718 | 719 | [[package]] 720 | name = "futures-lite" 721 | version = "1.13.0" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 724 | dependencies = [ 725 | "fastrand 1.9.0", 726 | "futures-core", 727 | "futures-io", 728 | "memchr", 729 | "parking", 730 | "pin-project-lite", 731 | "waker-fn", 732 | ] 733 | 734 | [[package]] 735 | name = "futures-macro" 736 | version = "0.3.29" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" 739 | dependencies = [ 740 | "proc-macro2", 741 | "quote", 742 | "syn 2.0.38", 743 | ] 744 | 745 | [[package]] 746 | name = "futures-sink" 747 | version = "0.3.29" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" 750 | 751 | [[package]] 752 | name = "futures-task" 753 | version = "0.3.29" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" 756 | 757 | [[package]] 758 | name = "futures-util" 759 | version = "0.3.29" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" 762 | dependencies = [ 763 | "futures-core", 764 | "futures-io", 765 | "futures-macro", 766 | "futures-sink", 767 | "futures-task", 768 | "memchr", 769 | "pin-project-lite", 770 | "pin-utils", 771 | "slab", 772 | ] 773 | 774 | [[package]] 775 | name = "generic-array" 776 | version = "0.14.7" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 779 | dependencies = [ 780 | "typenum", 781 | "version_check", 782 | ] 783 | 784 | [[package]] 785 | name = "getrandom" 786 | version = "0.2.10" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 789 | dependencies = [ 790 | "cfg-if", 791 | "libc", 792 | "wasi", 793 | ] 794 | 795 | [[package]] 796 | name = "gimli" 797 | version = "0.28.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" 800 | 801 | [[package]] 802 | name = "h2" 803 | version = "0.3.21" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" 806 | dependencies = [ 807 | "bytes", 808 | "fnv", 809 | "futures-core", 810 | "futures-sink", 811 | "futures-util", 812 | "http", 813 | "indexmap 1.9.3", 814 | "slab", 815 | "tokio", 816 | "tokio-util", 817 | "tracing", 818 | ] 819 | 820 | [[package]] 821 | name = "hashbrown" 822 | version = "0.12.3" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 825 | 826 | [[package]] 827 | name = "hashbrown" 828 | version = "0.14.2" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" 831 | 832 | [[package]] 833 | name = "heck" 834 | version = "0.4.1" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 837 | 838 | [[package]] 839 | name = "hermit-abi" 840 | version = "0.3.3" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" 843 | 844 | [[package]] 845 | name = "hex" 846 | version = "0.4.3" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 849 | 850 | [[package]] 851 | name = "hkdf" 852 | version = "0.12.3" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" 855 | dependencies = [ 856 | "hmac", 857 | ] 858 | 859 | [[package]] 860 | name = "hmac" 861 | version = "0.12.1" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 864 | dependencies = [ 865 | "digest", 866 | ] 867 | 868 | [[package]] 869 | name = "http" 870 | version = "0.2.9" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" 873 | dependencies = [ 874 | "bytes", 875 | "fnv", 876 | "itoa", 877 | ] 878 | 879 | [[package]] 880 | name = "http-body" 881 | version = "0.4.5" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 884 | dependencies = [ 885 | "bytes", 886 | "http", 887 | "pin-project-lite", 888 | ] 889 | 890 | [[package]] 891 | name = "httparse" 892 | version = "1.8.0" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 895 | 896 | [[package]] 897 | name = "httpdate" 898 | version = "1.0.3" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 901 | 902 | [[package]] 903 | name = "humantime" 904 | version = "2.1.0" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 907 | 908 | [[package]] 909 | name = "hyper" 910 | version = "0.14.27" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" 913 | dependencies = [ 914 | "bytes", 915 | "futures-channel", 916 | "futures-core", 917 | "futures-util", 918 | "h2", 919 | "http", 920 | "http-body", 921 | "httparse", 922 | "httpdate", 923 | "itoa", 924 | "pin-project-lite", 925 | "socket2 0.4.10", 926 | "tokio", 927 | "tower-service", 928 | "tracing", 929 | "want", 930 | ] 931 | 932 | [[package]] 933 | name = "hyper-tls" 934 | version = "0.5.0" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 937 | dependencies = [ 938 | "bytes", 939 | "hyper", 940 | "native-tls", 941 | "tokio", 942 | "tokio-native-tls", 943 | ] 944 | 945 | [[package]] 946 | name = "idna" 947 | version = "0.4.0" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" 950 | dependencies = [ 951 | "unicode-bidi", 952 | "unicode-normalization", 953 | ] 954 | 955 | [[package]] 956 | name = "indexmap" 957 | version = "1.9.3" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 960 | dependencies = [ 961 | "autocfg", 962 | "hashbrown 0.12.3", 963 | ] 964 | 965 | [[package]] 966 | name = "indexmap" 967 | version = "2.0.2" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" 970 | dependencies = [ 971 | "equivalent", 972 | "hashbrown 0.14.2", 973 | ] 974 | 975 | [[package]] 976 | name = "inquire" 977 | version = "0.6.2" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "c33e7c1ddeb15c9abcbfef6029d8e29f69b52b6d6c891031b88ed91b5065803b" 980 | dependencies = [ 981 | "bitflags 1.3.2", 982 | "crossterm", 983 | "dyn-clone", 984 | "lazy_static", 985 | "newline-converter", 986 | "thiserror", 987 | "unicode-segmentation", 988 | "unicode-width", 989 | ] 990 | 991 | [[package]] 992 | name = "instant" 993 | version = "0.1.12" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 996 | dependencies = [ 997 | "cfg-if", 998 | ] 999 | 1000 | [[package]] 1001 | name = "io-lifetimes" 1002 | version = "1.0.11" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" 1005 | dependencies = [ 1006 | "hermit-abi", 1007 | "libc", 1008 | "windows-sys", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "ipnet" 1013 | version = "2.9.0" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" 1016 | 1017 | [[package]] 1018 | name = "is-terminal" 1019 | version = "0.4.9" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" 1022 | dependencies = [ 1023 | "hermit-abi", 1024 | "rustix 0.38.21", 1025 | "windows-sys", 1026 | ] 1027 | 1028 | [[package]] 1029 | name = "itoa" 1030 | version = "1.0.9" 1031 | source = "registry+https://github.com/rust-lang/crates.io-index" 1032 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 1033 | 1034 | [[package]] 1035 | name = "js-sys" 1036 | version = "0.3.64" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 1039 | dependencies = [ 1040 | "wasm-bindgen", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "kasm" 1045 | version = "1.0.0" 1046 | dependencies = [ 1047 | "clap", 1048 | "csv", 1049 | "inquire", 1050 | "keyring", 1051 | "log", 1052 | "pretty_env_logger", 1053 | "regex", 1054 | "reqwest", 1055 | "serde", 1056 | "serde_json", 1057 | "strum", 1058 | "toml", 1059 | "whoami", 1060 | "zip", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "keyring" 1065 | version = "2.0.5" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "9549a129bd08149e0a71b2d1ce2729780d47127991bfd0a78cc1df697ec72492" 1068 | dependencies = [ 1069 | "byteorder", 1070 | "lazy_static", 1071 | "linux-keyutils", 1072 | "secret-service", 1073 | "security-framework", 1074 | "winapi", 1075 | ] 1076 | 1077 | [[package]] 1078 | name = "lazy_static" 1079 | version = "1.4.0" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1082 | 1083 | [[package]] 1084 | name = "libc" 1085 | version = "0.2.149" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" 1088 | 1089 | [[package]] 1090 | name = "linux-keyutils" 1091 | version = "0.2.3" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" 1094 | dependencies = [ 1095 | "bitflags 1.3.2", 1096 | "libc", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "linux-raw-sys" 1101 | version = "0.3.8" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" 1104 | 1105 | [[package]] 1106 | name = "linux-raw-sys" 1107 | version = "0.4.10" 1108 | source = "registry+https://github.com/rust-lang/crates.io-index" 1109 | checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" 1110 | 1111 | [[package]] 1112 | name = "lock_api" 1113 | version = "0.4.11" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 1116 | dependencies = [ 1117 | "autocfg", 1118 | "scopeguard", 1119 | ] 1120 | 1121 | [[package]] 1122 | name = "log" 1123 | version = "0.4.20" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 1126 | 1127 | [[package]] 1128 | name = "memchr" 1129 | version = "2.6.4" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 1132 | 1133 | [[package]] 1134 | name = "memoffset" 1135 | version = "0.7.1" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 1138 | dependencies = [ 1139 | "autocfg", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "mime" 1144 | version = "0.3.17" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1147 | 1148 | [[package]] 1149 | name = "miniz_oxide" 1150 | version = "0.7.1" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 1153 | dependencies = [ 1154 | "adler", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "mio" 1159 | version = "0.8.9" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" 1162 | dependencies = [ 1163 | "libc", 1164 | "log", 1165 | "wasi", 1166 | "windows-sys", 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "native-tls" 1171 | version = "0.2.11" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" 1174 | dependencies = [ 1175 | "lazy_static", 1176 | "libc", 1177 | "log", 1178 | "openssl", 1179 | "openssl-probe", 1180 | "openssl-sys", 1181 | "schannel", 1182 | "security-framework", 1183 | "security-framework-sys", 1184 | "tempfile", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "newline-converter" 1189 | version = "0.2.2" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" 1192 | dependencies = [ 1193 | "unicode-segmentation", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "nix" 1198 | version = "0.26.4" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 1201 | dependencies = [ 1202 | "bitflags 1.3.2", 1203 | "cfg-if", 1204 | "libc", 1205 | "memoffset", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "num" 1210 | version = "0.4.1" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" 1213 | dependencies = [ 1214 | "num-bigint", 1215 | "num-complex", 1216 | "num-integer", 1217 | "num-iter", 1218 | "num-rational", 1219 | "num-traits", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "num-bigint" 1224 | version = "0.4.4" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" 1227 | dependencies = [ 1228 | "autocfg", 1229 | "num-integer", 1230 | "num-traits", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "num-complex" 1235 | version = "0.4.4" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" 1238 | dependencies = [ 1239 | "num-traits", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "num-integer" 1244 | version = "0.1.45" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 1247 | dependencies = [ 1248 | "autocfg", 1249 | "num-traits", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "num-iter" 1254 | version = "0.1.43" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 1257 | dependencies = [ 1258 | "autocfg", 1259 | "num-integer", 1260 | "num-traits", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "num-rational" 1265 | version = "0.4.1" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 1268 | dependencies = [ 1269 | "autocfg", 1270 | "num-bigint", 1271 | "num-integer", 1272 | "num-traits", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "num-traits" 1277 | version = "0.2.17" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 1280 | dependencies = [ 1281 | "autocfg", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "num_cpus" 1286 | version = "1.16.0" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1289 | dependencies = [ 1290 | "hermit-abi", 1291 | "libc", 1292 | ] 1293 | 1294 | [[package]] 1295 | name = "object" 1296 | version = "0.32.1" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" 1299 | dependencies = [ 1300 | "memchr", 1301 | ] 1302 | 1303 | [[package]] 1304 | name = "once_cell" 1305 | version = "1.18.0" 1306 | source = "registry+https://github.com/rust-lang/crates.io-index" 1307 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 1308 | 1309 | [[package]] 1310 | name = "opaque-debug" 1311 | version = "0.3.0" 1312 | source = "registry+https://github.com/rust-lang/crates.io-index" 1313 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 1314 | 1315 | [[package]] 1316 | name = "openssl" 1317 | version = "0.10.57" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" 1320 | dependencies = [ 1321 | "bitflags 2.4.1", 1322 | "cfg-if", 1323 | "foreign-types", 1324 | "libc", 1325 | "once_cell", 1326 | "openssl-macros", 1327 | "openssl-sys", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "openssl-macros" 1332 | version = "0.1.1" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1335 | dependencies = [ 1336 | "proc-macro2", 1337 | "quote", 1338 | "syn 2.0.38", 1339 | ] 1340 | 1341 | [[package]] 1342 | name = "openssl-probe" 1343 | version = "0.1.5" 1344 | source = "registry+https://github.com/rust-lang/crates.io-index" 1345 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1346 | 1347 | [[package]] 1348 | name = "openssl-sys" 1349 | version = "0.9.93" 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" 1351 | checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" 1352 | dependencies = [ 1353 | "cc", 1354 | "libc", 1355 | "pkg-config", 1356 | "vcpkg", 1357 | ] 1358 | 1359 | [[package]] 1360 | name = "ordered-stream" 1361 | version = "0.2.0" 1362 | source = "registry+https://github.com/rust-lang/crates.io-index" 1363 | checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" 1364 | dependencies = [ 1365 | "futures-core", 1366 | "pin-project-lite", 1367 | ] 1368 | 1369 | [[package]] 1370 | name = "parking" 1371 | version = "2.2.0" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" 1374 | 1375 | [[package]] 1376 | name = "parking_lot" 1377 | version = "0.12.1" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 1380 | dependencies = [ 1381 | "lock_api", 1382 | "parking_lot_core", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "parking_lot_core" 1387 | version = "0.9.9" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 1390 | dependencies = [ 1391 | "cfg-if", 1392 | "libc", 1393 | "redox_syscall", 1394 | "smallvec", 1395 | "windows-targets", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "percent-encoding" 1400 | version = "2.3.0" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" 1403 | 1404 | [[package]] 1405 | name = "pin-project-lite" 1406 | version = "0.2.13" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 1409 | 1410 | [[package]] 1411 | name = "pin-utils" 1412 | version = "0.1.0" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1415 | 1416 | [[package]] 1417 | name = "piper" 1418 | version = "0.2.1" 1419 | source = "registry+https://github.com/rust-lang/crates.io-index" 1420 | checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" 1421 | dependencies = [ 1422 | "atomic-waker", 1423 | "fastrand 2.0.1", 1424 | "futures-io", 1425 | ] 1426 | 1427 | [[package]] 1428 | name = "pkg-config" 1429 | version = "0.3.27" 1430 | source = "registry+https://github.com/rust-lang/crates.io-index" 1431 | checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" 1432 | 1433 | [[package]] 1434 | name = "polling" 1435 | version = "2.8.0" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 1438 | dependencies = [ 1439 | "autocfg", 1440 | "bitflags 1.3.2", 1441 | "cfg-if", 1442 | "concurrent-queue", 1443 | "libc", 1444 | "log", 1445 | "pin-project-lite", 1446 | "windows-sys", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "polling" 1451 | version = "3.3.0" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" 1454 | dependencies = [ 1455 | "cfg-if", 1456 | "concurrent-queue", 1457 | "pin-project-lite", 1458 | "rustix 0.38.21", 1459 | "tracing", 1460 | "windows-sys", 1461 | ] 1462 | 1463 | [[package]] 1464 | name = "powerfmt" 1465 | version = "0.2.0" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1468 | 1469 | [[package]] 1470 | name = "ppv-lite86" 1471 | version = "0.2.17" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1474 | 1475 | [[package]] 1476 | name = "pretty_env_logger" 1477 | version = "0.5.0" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" 1480 | dependencies = [ 1481 | "env_logger", 1482 | "log", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "proc-macro-crate" 1487 | version = "1.3.1" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 1490 | dependencies = [ 1491 | "once_cell", 1492 | "toml_edit 0.19.15", 1493 | ] 1494 | 1495 | [[package]] 1496 | name = "proc-macro2" 1497 | version = "1.0.69" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 1500 | dependencies = [ 1501 | "unicode-ident", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "quote" 1506 | version = "1.0.33" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 1509 | dependencies = [ 1510 | "proc-macro2", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "rand" 1515 | version = "0.8.5" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1518 | dependencies = [ 1519 | "libc", 1520 | "rand_chacha", 1521 | "rand_core", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "rand_chacha" 1526 | version = "0.3.1" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1529 | dependencies = [ 1530 | "ppv-lite86", 1531 | "rand_core", 1532 | ] 1533 | 1534 | [[package]] 1535 | name = "rand_core" 1536 | version = "0.6.4" 1537 | source = "registry+https://github.com/rust-lang/crates.io-index" 1538 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1539 | dependencies = [ 1540 | "getrandom", 1541 | ] 1542 | 1543 | [[package]] 1544 | name = "redox_syscall" 1545 | version = "0.4.1" 1546 | source = "registry+https://github.com/rust-lang/crates.io-index" 1547 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 1548 | dependencies = [ 1549 | "bitflags 1.3.2", 1550 | ] 1551 | 1552 | [[package]] 1553 | name = "regex" 1554 | version = "1.10.2" 1555 | source = "registry+https://github.com/rust-lang/crates.io-index" 1556 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 1557 | dependencies = [ 1558 | "aho-corasick", 1559 | "memchr", 1560 | "regex-automata", 1561 | "regex-syntax", 1562 | ] 1563 | 1564 | [[package]] 1565 | name = "regex-automata" 1566 | version = "0.4.3" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 1569 | dependencies = [ 1570 | "aho-corasick", 1571 | "memchr", 1572 | "regex-syntax", 1573 | ] 1574 | 1575 | [[package]] 1576 | name = "regex-syntax" 1577 | version = "0.8.2" 1578 | source = "registry+https://github.com/rust-lang/crates.io-index" 1579 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 1580 | 1581 | [[package]] 1582 | name = "reqwest" 1583 | version = "0.11.22" 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" 1585 | checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" 1586 | dependencies = [ 1587 | "base64", 1588 | "bytes", 1589 | "encoding_rs", 1590 | "futures-core", 1591 | "futures-util", 1592 | "h2", 1593 | "http", 1594 | "http-body", 1595 | "hyper", 1596 | "hyper-tls", 1597 | "ipnet", 1598 | "js-sys", 1599 | "log", 1600 | "mime", 1601 | "native-tls", 1602 | "once_cell", 1603 | "percent-encoding", 1604 | "pin-project-lite", 1605 | "serde", 1606 | "serde_json", 1607 | "serde_urlencoded", 1608 | "system-configuration", 1609 | "tokio", 1610 | "tokio-native-tls", 1611 | "tower-service", 1612 | "url", 1613 | "wasm-bindgen", 1614 | "wasm-bindgen-futures", 1615 | "web-sys", 1616 | "winreg", 1617 | ] 1618 | 1619 | [[package]] 1620 | name = "rustc-demangle" 1621 | version = "0.1.23" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 1624 | 1625 | [[package]] 1626 | name = "rustix" 1627 | version = "0.37.27" 1628 | source = "registry+https://github.com/rust-lang/crates.io-index" 1629 | checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" 1630 | dependencies = [ 1631 | "bitflags 1.3.2", 1632 | "errno", 1633 | "io-lifetimes", 1634 | "libc", 1635 | "linux-raw-sys 0.3.8", 1636 | "windows-sys", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "rustix" 1641 | version = "0.38.21" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" 1644 | dependencies = [ 1645 | "bitflags 2.4.1", 1646 | "errno", 1647 | "libc", 1648 | "linux-raw-sys 0.4.10", 1649 | "windows-sys", 1650 | ] 1651 | 1652 | [[package]] 1653 | name = "rustversion" 1654 | version = "1.0.14" 1655 | source = "registry+https://github.com/rust-lang/crates.io-index" 1656 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 1657 | 1658 | [[package]] 1659 | name = "ryu" 1660 | version = "1.0.15" 1661 | source = "registry+https://github.com/rust-lang/crates.io-index" 1662 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 1663 | 1664 | [[package]] 1665 | name = "schannel" 1666 | version = "0.1.22" 1667 | source = "registry+https://github.com/rust-lang/crates.io-index" 1668 | checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" 1669 | dependencies = [ 1670 | "windows-sys", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "scopeguard" 1675 | version = "1.2.0" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1678 | 1679 | [[package]] 1680 | name = "secret-service" 1681 | version = "3.0.1" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" 1684 | dependencies = [ 1685 | "aes", 1686 | "block-modes", 1687 | "futures-util", 1688 | "generic-array", 1689 | "hkdf", 1690 | "num", 1691 | "once_cell", 1692 | "rand", 1693 | "serde", 1694 | "sha2", 1695 | "zbus", 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "security-framework" 1700 | version = "2.9.2" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" 1703 | dependencies = [ 1704 | "bitflags 1.3.2", 1705 | "core-foundation", 1706 | "core-foundation-sys", 1707 | "libc", 1708 | "security-framework-sys", 1709 | ] 1710 | 1711 | [[package]] 1712 | name = "security-framework-sys" 1713 | version = "2.9.1" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" 1716 | dependencies = [ 1717 | "core-foundation-sys", 1718 | "libc", 1719 | ] 1720 | 1721 | [[package]] 1722 | name = "serde" 1723 | version = "1.0.190" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" 1726 | dependencies = [ 1727 | "serde_derive", 1728 | ] 1729 | 1730 | [[package]] 1731 | name = "serde_derive" 1732 | version = "1.0.190" 1733 | source = "registry+https://github.com/rust-lang/crates.io-index" 1734 | checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" 1735 | dependencies = [ 1736 | "proc-macro2", 1737 | "quote", 1738 | "syn 2.0.38", 1739 | ] 1740 | 1741 | [[package]] 1742 | name = "serde_json" 1743 | version = "1.0.108" 1744 | source = "registry+https://github.com/rust-lang/crates.io-index" 1745 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 1746 | dependencies = [ 1747 | "itoa", 1748 | "ryu", 1749 | "serde", 1750 | ] 1751 | 1752 | [[package]] 1753 | name = "serde_repr" 1754 | version = "0.1.17" 1755 | source = "registry+https://github.com/rust-lang/crates.io-index" 1756 | checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" 1757 | dependencies = [ 1758 | "proc-macro2", 1759 | "quote", 1760 | "syn 2.0.38", 1761 | ] 1762 | 1763 | [[package]] 1764 | name = "serde_spanned" 1765 | version = "0.6.4" 1766 | source = "registry+https://github.com/rust-lang/crates.io-index" 1767 | checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" 1768 | dependencies = [ 1769 | "serde", 1770 | ] 1771 | 1772 | [[package]] 1773 | name = "serde_urlencoded" 1774 | version = "0.7.1" 1775 | source = "registry+https://github.com/rust-lang/crates.io-index" 1776 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1777 | dependencies = [ 1778 | "form_urlencoded", 1779 | "itoa", 1780 | "ryu", 1781 | "serde", 1782 | ] 1783 | 1784 | [[package]] 1785 | name = "sha1" 1786 | version = "0.10.6" 1787 | source = "registry+https://github.com/rust-lang/crates.io-index" 1788 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1789 | dependencies = [ 1790 | "cfg-if", 1791 | "cpufeatures", 1792 | "digest", 1793 | ] 1794 | 1795 | [[package]] 1796 | name = "sha2" 1797 | version = "0.10.8" 1798 | source = "registry+https://github.com/rust-lang/crates.io-index" 1799 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1800 | dependencies = [ 1801 | "cfg-if", 1802 | "cpufeatures", 1803 | "digest", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "signal-hook" 1808 | version = "0.3.17" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 1811 | dependencies = [ 1812 | "libc", 1813 | "signal-hook-registry", 1814 | ] 1815 | 1816 | [[package]] 1817 | name = "signal-hook-mio" 1818 | version = "0.2.3" 1819 | source = "registry+https://github.com/rust-lang/crates.io-index" 1820 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 1821 | dependencies = [ 1822 | "libc", 1823 | "mio", 1824 | "signal-hook", 1825 | ] 1826 | 1827 | [[package]] 1828 | name = "signal-hook-registry" 1829 | version = "1.4.1" 1830 | source = "registry+https://github.com/rust-lang/crates.io-index" 1831 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 1832 | dependencies = [ 1833 | "libc", 1834 | ] 1835 | 1836 | [[package]] 1837 | name = "slab" 1838 | version = "0.4.9" 1839 | source = "registry+https://github.com/rust-lang/crates.io-index" 1840 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1841 | dependencies = [ 1842 | "autocfg", 1843 | ] 1844 | 1845 | [[package]] 1846 | name = "smallvec" 1847 | version = "1.11.1" 1848 | source = "registry+https://github.com/rust-lang/crates.io-index" 1849 | checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" 1850 | 1851 | [[package]] 1852 | name = "socket2" 1853 | version = "0.4.10" 1854 | source = "registry+https://github.com/rust-lang/crates.io-index" 1855 | checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" 1856 | dependencies = [ 1857 | "libc", 1858 | "winapi", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "socket2" 1863 | version = "0.5.5" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" 1866 | dependencies = [ 1867 | "libc", 1868 | "windows-sys", 1869 | ] 1870 | 1871 | [[package]] 1872 | name = "static_assertions" 1873 | version = "1.1.0" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1876 | 1877 | [[package]] 1878 | name = "strsim" 1879 | version = "0.10.0" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1882 | 1883 | [[package]] 1884 | name = "strum" 1885 | version = "0.25.0" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" 1888 | dependencies = [ 1889 | "strum_macros", 1890 | ] 1891 | 1892 | [[package]] 1893 | name = "strum_macros" 1894 | version = "0.25.3" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" 1897 | dependencies = [ 1898 | "heck", 1899 | "proc-macro2", 1900 | "quote", 1901 | "rustversion", 1902 | "syn 2.0.38", 1903 | ] 1904 | 1905 | [[package]] 1906 | name = "subtle" 1907 | version = "2.5.0" 1908 | source = "registry+https://github.com/rust-lang/crates.io-index" 1909 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 1910 | 1911 | [[package]] 1912 | name = "syn" 1913 | version = "1.0.109" 1914 | source = "registry+https://github.com/rust-lang/crates.io-index" 1915 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1916 | dependencies = [ 1917 | "proc-macro2", 1918 | "quote", 1919 | "unicode-ident", 1920 | ] 1921 | 1922 | [[package]] 1923 | name = "syn" 1924 | version = "2.0.38" 1925 | source = "registry+https://github.com/rust-lang/crates.io-index" 1926 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 1927 | dependencies = [ 1928 | "proc-macro2", 1929 | "quote", 1930 | "unicode-ident", 1931 | ] 1932 | 1933 | [[package]] 1934 | name = "system-configuration" 1935 | version = "0.5.1" 1936 | source = "registry+https://github.com/rust-lang/crates.io-index" 1937 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 1938 | dependencies = [ 1939 | "bitflags 1.3.2", 1940 | "core-foundation", 1941 | "system-configuration-sys", 1942 | ] 1943 | 1944 | [[package]] 1945 | name = "system-configuration-sys" 1946 | version = "0.5.0" 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" 1948 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 1949 | dependencies = [ 1950 | "core-foundation-sys", 1951 | "libc", 1952 | ] 1953 | 1954 | [[package]] 1955 | name = "tempfile" 1956 | version = "3.8.1" 1957 | source = "registry+https://github.com/rust-lang/crates.io-index" 1958 | checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" 1959 | dependencies = [ 1960 | "cfg-if", 1961 | "fastrand 2.0.1", 1962 | "redox_syscall", 1963 | "rustix 0.38.21", 1964 | "windows-sys", 1965 | ] 1966 | 1967 | [[package]] 1968 | name = "termcolor" 1969 | version = "1.3.0" 1970 | source = "registry+https://github.com/rust-lang/crates.io-index" 1971 | checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" 1972 | dependencies = [ 1973 | "winapi-util", 1974 | ] 1975 | 1976 | [[package]] 1977 | name = "thiserror" 1978 | version = "1.0.50" 1979 | source = "registry+https://github.com/rust-lang/crates.io-index" 1980 | checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" 1981 | dependencies = [ 1982 | "thiserror-impl", 1983 | ] 1984 | 1985 | [[package]] 1986 | name = "thiserror-impl" 1987 | version = "1.0.50" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" 1990 | dependencies = [ 1991 | "proc-macro2", 1992 | "quote", 1993 | "syn 2.0.38", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "time" 1998 | version = "0.3.30" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" 2001 | dependencies = [ 2002 | "deranged", 2003 | "powerfmt", 2004 | "serde", 2005 | "time-core", 2006 | ] 2007 | 2008 | [[package]] 2009 | name = "time-core" 2010 | version = "0.1.2" 2011 | source = "registry+https://github.com/rust-lang/crates.io-index" 2012 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 2013 | 2014 | [[package]] 2015 | name = "tinyvec" 2016 | version = "1.6.0" 2017 | source = "registry+https://github.com/rust-lang/crates.io-index" 2018 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 2019 | dependencies = [ 2020 | "tinyvec_macros", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "tinyvec_macros" 2025 | version = "0.1.1" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2028 | 2029 | [[package]] 2030 | name = "tokio" 2031 | version = "1.33.0" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" 2034 | dependencies = [ 2035 | "backtrace", 2036 | "bytes", 2037 | "libc", 2038 | "mio", 2039 | "num_cpus", 2040 | "pin-project-lite", 2041 | "socket2 0.5.5", 2042 | "windows-sys", 2043 | ] 2044 | 2045 | [[package]] 2046 | name = "tokio-native-tls" 2047 | version = "0.3.1" 2048 | source = "registry+https://github.com/rust-lang/crates.io-index" 2049 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2050 | dependencies = [ 2051 | "native-tls", 2052 | "tokio", 2053 | ] 2054 | 2055 | [[package]] 2056 | name = "tokio-util" 2057 | version = "0.7.10" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" 2060 | dependencies = [ 2061 | "bytes", 2062 | "futures-core", 2063 | "futures-sink", 2064 | "pin-project-lite", 2065 | "tokio", 2066 | "tracing", 2067 | ] 2068 | 2069 | [[package]] 2070 | name = "toml" 2071 | version = "0.8.6" 2072 | source = "registry+https://github.com/rust-lang/crates.io-index" 2073 | checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" 2074 | dependencies = [ 2075 | "serde", 2076 | "serde_spanned", 2077 | "toml_datetime", 2078 | "toml_edit 0.20.7", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "toml_datetime" 2083 | version = "0.6.5" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 2086 | dependencies = [ 2087 | "serde", 2088 | ] 2089 | 2090 | [[package]] 2091 | name = "toml_edit" 2092 | version = "0.19.15" 2093 | source = "registry+https://github.com/rust-lang/crates.io-index" 2094 | checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" 2095 | dependencies = [ 2096 | "indexmap 2.0.2", 2097 | "toml_datetime", 2098 | "winnow", 2099 | ] 2100 | 2101 | [[package]] 2102 | name = "toml_edit" 2103 | version = "0.20.7" 2104 | source = "registry+https://github.com/rust-lang/crates.io-index" 2105 | checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" 2106 | dependencies = [ 2107 | "indexmap 2.0.2", 2108 | "serde", 2109 | "serde_spanned", 2110 | "toml_datetime", 2111 | "winnow", 2112 | ] 2113 | 2114 | [[package]] 2115 | name = "tower-service" 2116 | version = "0.3.2" 2117 | source = "registry+https://github.com/rust-lang/crates.io-index" 2118 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 2119 | 2120 | [[package]] 2121 | name = "tracing" 2122 | version = "0.1.40" 2123 | source = "registry+https://github.com/rust-lang/crates.io-index" 2124 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2125 | dependencies = [ 2126 | "pin-project-lite", 2127 | "tracing-attributes", 2128 | "tracing-core", 2129 | ] 2130 | 2131 | [[package]] 2132 | name = "tracing-attributes" 2133 | version = "0.1.27" 2134 | source = "registry+https://github.com/rust-lang/crates.io-index" 2135 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2136 | dependencies = [ 2137 | "proc-macro2", 2138 | "quote", 2139 | "syn 2.0.38", 2140 | ] 2141 | 2142 | [[package]] 2143 | name = "tracing-core" 2144 | version = "0.1.32" 2145 | source = "registry+https://github.com/rust-lang/crates.io-index" 2146 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2147 | dependencies = [ 2148 | "once_cell", 2149 | ] 2150 | 2151 | [[package]] 2152 | name = "try-lock" 2153 | version = "0.2.4" 2154 | source = "registry+https://github.com/rust-lang/crates.io-index" 2155 | checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" 2156 | 2157 | [[package]] 2158 | name = "typenum" 2159 | version = "1.17.0" 2160 | source = "registry+https://github.com/rust-lang/crates.io-index" 2161 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2162 | 2163 | [[package]] 2164 | name = "uds_windows" 2165 | version = "1.0.2" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" 2168 | dependencies = [ 2169 | "tempfile", 2170 | "winapi", 2171 | ] 2172 | 2173 | [[package]] 2174 | name = "unicode-bidi" 2175 | version = "0.3.13" 2176 | source = "registry+https://github.com/rust-lang/crates.io-index" 2177 | checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" 2178 | 2179 | [[package]] 2180 | name = "unicode-ident" 2181 | version = "1.0.12" 2182 | source = "registry+https://github.com/rust-lang/crates.io-index" 2183 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 2184 | 2185 | [[package]] 2186 | name = "unicode-normalization" 2187 | version = "0.1.22" 2188 | source = "registry+https://github.com/rust-lang/crates.io-index" 2189 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" 2190 | dependencies = [ 2191 | "tinyvec", 2192 | ] 2193 | 2194 | [[package]] 2195 | name = "unicode-segmentation" 2196 | version = "1.10.1" 2197 | source = "registry+https://github.com/rust-lang/crates.io-index" 2198 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 2199 | 2200 | [[package]] 2201 | name = "unicode-width" 2202 | version = "0.1.11" 2203 | source = "registry+https://github.com/rust-lang/crates.io-index" 2204 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 2205 | 2206 | [[package]] 2207 | name = "url" 2208 | version = "2.4.1" 2209 | source = "registry+https://github.com/rust-lang/crates.io-index" 2210 | checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" 2211 | dependencies = [ 2212 | "form_urlencoded", 2213 | "idna", 2214 | "percent-encoding", 2215 | ] 2216 | 2217 | [[package]] 2218 | name = "utf8parse" 2219 | version = "0.2.1" 2220 | source = "registry+https://github.com/rust-lang/crates.io-index" 2221 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 2222 | 2223 | [[package]] 2224 | name = "vcpkg" 2225 | version = "0.2.15" 2226 | source = "registry+https://github.com/rust-lang/crates.io-index" 2227 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2228 | 2229 | [[package]] 2230 | name = "version_check" 2231 | version = "0.9.4" 2232 | source = "registry+https://github.com/rust-lang/crates.io-index" 2233 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2234 | 2235 | [[package]] 2236 | name = "waker-fn" 2237 | version = "1.1.1" 2238 | source = "registry+https://github.com/rust-lang/crates.io-index" 2239 | checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" 2240 | 2241 | [[package]] 2242 | name = "want" 2243 | version = "0.3.1" 2244 | source = "registry+https://github.com/rust-lang/crates.io-index" 2245 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2246 | dependencies = [ 2247 | "try-lock", 2248 | ] 2249 | 2250 | [[package]] 2251 | name = "wasi" 2252 | version = "0.11.0+wasi-snapshot-preview1" 2253 | source = "registry+https://github.com/rust-lang/crates.io-index" 2254 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2255 | 2256 | [[package]] 2257 | name = "wasm-bindgen" 2258 | version = "0.2.87" 2259 | source = "registry+https://github.com/rust-lang/crates.io-index" 2260 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 2261 | dependencies = [ 2262 | "cfg-if", 2263 | "wasm-bindgen-macro", 2264 | ] 2265 | 2266 | [[package]] 2267 | name = "wasm-bindgen-backend" 2268 | version = "0.2.87" 2269 | source = "registry+https://github.com/rust-lang/crates.io-index" 2270 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 2271 | dependencies = [ 2272 | "bumpalo", 2273 | "log", 2274 | "once_cell", 2275 | "proc-macro2", 2276 | "quote", 2277 | "syn 2.0.38", 2278 | "wasm-bindgen-shared", 2279 | ] 2280 | 2281 | [[package]] 2282 | name = "wasm-bindgen-futures" 2283 | version = "0.4.37" 2284 | source = "registry+https://github.com/rust-lang/crates.io-index" 2285 | checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" 2286 | dependencies = [ 2287 | "cfg-if", 2288 | "js-sys", 2289 | "wasm-bindgen", 2290 | "web-sys", 2291 | ] 2292 | 2293 | [[package]] 2294 | name = "wasm-bindgen-macro" 2295 | version = "0.2.87" 2296 | source = "registry+https://github.com/rust-lang/crates.io-index" 2297 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 2298 | dependencies = [ 2299 | "quote", 2300 | "wasm-bindgen-macro-support", 2301 | ] 2302 | 2303 | [[package]] 2304 | name = "wasm-bindgen-macro-support" 2305 | version = "0.2.87" 2306 | source = "registry+https://github.com/rust-lang/crates.io-index" 2307 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 2308 | dependencies = [ 2309 | "proc-macro2", 2310 | "quote", 2311 | "syn 2.0.38", 2312 | "wasm-bindgen-backend", 2313 | "wasm-bindgen-shared", 2314 | ] 2315 | 2316 | [[package]] 2317 | name = "wasm-bindgen-shared" 2318 | version = "0.2.87" 2319 | source = "registry+https://github.com/rust-lang/crates.io-index" 2320 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 2321 | 2322 | [[package]] 2323 | name = "web-sys" 2324 | version = "0.3.64" 2325 | source = "registry+https://github.com/rust-lang/crates.io-index" 2326 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 2327 | dependencies = [ 2328 | "js-sys", 2329 | "wasm-bindgen", 2330 | ] 2331 | 2332 | [[package]] 2333 | name = "whoami" 2334 | version = "1.4.1" 2335 | source = "registry+https://github.com/rust-lang/crates.io-index" 2336 | checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" 2337 | dependencies = [ 2338 | "wasm-bindgen", 2339 | "web-sys", 2340 | ] 2341 | 2342 | [[package]] 2343 | name = "winapi" 2344 | version = "0.3.9" 2345 | source = "registry+https://github.com/rust-lang/crates.io-index" 2346 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2347 | dependencies = [ 2348 | "winapi-i686-pc-windows-gnu", 2349 | "winapi-x86_64-pc-windows-gnu", 2350 | ] 2351 | 2352 | [[package]] 2353 | name = "winapi-i686-pc-windows-gnu" 2354 | version = "0.4.0" 2355 | source = "registry+https://github.com/rust-lang/crates.io-index" 2356 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2357 | 2358 | [[package]] 2359 | name = "winapi-util" 2360 | version = "0.1.6" 2361 | source = "registry+https://github.com/rust-lang/crates.io-index" 2362 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 2363 | dependencies = [ 2364 | "winapi", 2365 | ] 2366 | 2367 | [[package]] 2368 | name = "winapi-x86_64-pc-windows-gnu" 2369 | version = "0.4.0" 2370 | source = "registry+https://github.com/rust-lang/crates.io-index" 2371 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2372 | 2373 | [[package]] 2374 | name = "windows-sys" 2375 | version = "0.48.0" 2376 | source = "registry+https://github.com/rust-lang/crates.io-index" 2377 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 2378 | dependencies = [ 2379 | "windows-targets", 2380 | ] 2381 | 2382 | [[package]] 2383 | name = "windows-targets" 2384 | version = "0.48.5" 2385 | source = "registry+https://github.com/rust-lang/crates.io-index" 2386 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 2387 | dependencies = [ 2388 | "windows_aarch64_gnullvm", 2389 | "windows_aarch64_msvc", 2390 | "windows_i686_gnu", 2391 | "windows_i686_msvc", 2392 | "windows_x86_64_gnu", 2393 | "windows_x86_64_gnullvm", 2394 | "windows_x86_64_msvc", 2395 | ] 2396 | 2397 | [[package]] 2398 | name = "windows_aarch64_gnullvm" 2399 | version = "0.48.5" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2402 | 2403 | [[package]] 2404 | name = "windows_aarch64_msvc" 2405 | version = "0.48.5" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2408 | 2409 | [[package]] 2410 | name = "windows_i686_gnu" 2411 | version = "0.48.5" 2412 | source = "registry+https://github.com/rust-lang/crates.io-index" 2413 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2414 | 2415 | [[package]] 2416 | name = "windows_i686_msvc" 2417 | version = "0.48.5" 2418 | source = "registry+https://github.com/rust-lang/crates.io-index" 2419 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2420 | 2421 | [[package]] 2422 | name = "windows_x86_64_gnu" 2423 | version = "0.48.5" 2424 | source = "registry+https://github.com/rust-lang/crates.io-index" 2425 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2426 | 2427 | [[package]] 2428 | name = "windows_x86_64_gnullvm" 2429 | version = "0.48.5" 2430 | source = "registry+https://github.com/rust-lang/crates.io-index" 2431 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2432 | 2433 | [[package]] 2434 | name = "windows_x86_64_msvc" 2435 | version = "0.48.5" 2436 | source = "registry+https://github.com/rust-lang/crates.io-index" 2437 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2438 | 2439 | [[package]] 2440 | name = "winnow" 2441 | version = "0.5.17" 2442 | source = "registry+https://github.com/rust-lang/crates.io-index" 2443 | checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" 2444 | dependencies = [ 2445 | "memchr", 2446 | ] 2447 | 2448 | [[package]] 2449 | name = "winreg" 2450 | version = "0.50.0" 2451 | source = "registry+https://github.com/rust-lang/crates.io-index" 2452 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 2453 | dependencies = [ 2454 | "cfg-if", 2455 | "windows-sys", 2456 | ] 2457 | 2458 | [[package]] 2459 | name = "xdg-home" 2460 | version = "1.0.0" 2461 | source = "registry+https://github.com/rust-lang/crates.io-index" 2462 | checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" 2463 | dependencies = [ 2464 | "nix", 2465 | "winapi", 2466 | ] 2467 | 2468 | [[package]] 2469 | name = "zbus" 2470 | version = "3.14.1" 2471 | source = "registry+https://github.com/rust-lang/crates.io-index" 2472 | checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" 2473 | dependencies = [ 2474 | "async-broadcast", 2475 | "async-executor", 2476 | "async-fs", 2477 | "async-io 1.13.0", 2478 | "async-lock", 2479 | "async-process", 2480 | "async-recursion", 2481 | "async-task", 2482 | "async-trait", 2483 | "blocking", 2484 | "byteorder", 2485 | "derivative", 2486 | "enumflags2", 2487 | "event-listener 2.5.3", 2488 | "futures-core", 2489 | "futures-sink", 2490 | "futures-util", 2491 | "hex", 2492 | "nix", 2493 | "once_cell", 2494 | "ordered-stream", 2495 | "rand", 2496 | "serde", 2497 | "serde_repr", 2498 | "sha1", 2499 | "static_assertions", 2500 | "tracing", 2501 | "uds_windows", 2502 | "winapi", 2503 | "xdg-home", 2504 | "zbus_macros", 2505 | "zbus_names", 2506 | "zvariant", 2507 | ] 2508 | 2509 | [[package]] 2510 | name = "zbus_macros" 2511 | version = "3.14.1" 2512 | source = "registry+https://github.com/rust-lang/crates.io-index" 2513 | checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" 2514 | dependencies = [ 2515 | "proc-macro-crate", 2516 | "proc-macro2", 2517 | "quote", 2518 | "regex", 2519 | "syn 1.0.109", 2520 | "zvariant_utils", 2521 | ] 2522 | 2523 | [[package]] 2524 | name = "zbus_names" 2525 | version = "2.6.0" 2526 | source = "registry+https://github.com/rust-lang/crates.io-index" 2527 | checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" 2528 | dependencies = [ 2529 | "serde", 2530 | "static_assertions", 2531 | "zvariant", 2532 | ] 2533 | 2534 | [[package]] 2535 | name = "zip" 2536 | version = "0.6.6" 2537 | source = "registry+https://github.com/rust-lang/crates.io-index" 2538 | checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" 2539 | dependencies = [ 2540 | "byteorder", 2541 | "crc32fast", 2542 | "crossbeam-utils", 2543 | "flate2", 2544 | "time", 2545 | ] 2546 | 2547 | [[package]] 2548 | name = "zvariant" 2549 | version = "3.15.0" 2550 | source = "registry+https://github.com/rust-lang/crates.io-index" 2551 | checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" 2552 | dependencies = [ 2553 | "byteorder", 2554 | "enumflags2", 2555 | "libc", 2556 | "serde", 2557 | "static_assertions", 2558 | "zvariant_derive", 2559 | ] 2560 | 2561 | [[package]] 2562 | name = "zvariant_derive" 2563 | version = "3.15.0" 2564 | source = "registry+https://github.com/rust-lang/crates.io-index" 2565 | checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" 2566 | dependencies = [ 2567 | "proc-macro-crate", 2568 | "proc-macro2", 2569 | "quote", 2570 | "syn 1.0.109", 2571 | "zvariant_utils", 2572 | ] 2573 | 2574 | [[package]] 2575 | name = "zvariant_utils" 2576 | version = "1.0.1" 2577 | source = "registry+https://github.com/rust-lang/crates.io-index" 2578 | checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" 2579 | dependencies = [ 2580 | "proc-macro2", 2581 | "quote", 2582 | "syn 1.0.109", 2583 | ] 2584 | --------------------------------------------------------------------------------