2022-09-30 22:06:15 +00:00
|
|
|
use std::{fs::read_to_string, path::PathBuf};
|
2020-08-28 11:37:29 +00:00
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
use clap::Parser;
|
2020-08-28 11:37:29 +00:00
|
|
|
use opml::{Outline, OPML};
|
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
#[derive(Debug, Parser)]
|
|
|
|
#[clap(about, author, version)]
|
|
|
|
struct Args {
|
|
|
|
/// The OPML file to parse.
|
|
|
|
#[clap(short, long)]
|
|
|
|
file: PathBuf,
|
|
|
|
|
|
|
|
/// Output the OPML as JSON.
|
|
|
|
#[clap(long, group = "format", required = true)]
|
|
|
|
json: bool,
|
2020-08-28 11:37:29 +00:00
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
/// Output the OPML as pretty-printed JSON.
|
|
|
|
#[clap(long, group = "format", required = true)]
|
|
|
|
json_pretty: bool,
|
2020-08-28 11:37:29 +00:00
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
/// Only output the outline text and xmlUrl attributes when both are present
|
|
|
|
/// in the outline element.
|
|
|
|
#[clap(long, group = "format", required = true)]
|
|
|
|
rss: bool,
|
2020-08-28 11:37:29 +00:00
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
/// Print extra information while running.
|
|
|
|
#[clap(long)]
|
|
|
|
verbose: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let args = Args::parse();
|
2020-08-28 11:37:29 +00:00
|
|
|
|
|
|
|
// Read the file to string.
|
2022-09-30 22:06:15 +00:00
|
|
|
let xml = read_to_string(args.file).expect("Failed to read OPML file");
|
2020-08-28 11:37:29 +00:00
|
|
|
|
|
|
|
// Parse the OPML from the read file.
|
2021-03-23 12:02:16 +00:00
|
|
|
let opml = OPML::from_str(&xml).expect("Failed to parse OPML file");
|
2020-08-28 11:37:29 +00:00
|
|
|
|
2022-09-30 22:06:15 +00:00
|
|
|
if args.rss {
|
2020-08-28 11:37:29 +00:00
|
|
|
// Get all the outlines from the OPML document.
|
|
|
|
let outlines = extract_all_outlines(&opml.body.outlines);
|
|
|
|
|
|
|
|
// Print out the text and xmlUrl attributes when possible.
|
|
|
|
for outline in outlines {
|
|
|
|
if let Some(xml_url) = outline.xml_url {
|
|
|
|
println!("{}", outline.text);
|
|
|
|
println!("{}", xml_url);
|
2022-09-30 22:06:15 +00:00
|
|
|
} else if args.verbose {
|
2020-08-28 11:37:29 +00:00
|
|
|
println!(
|
|
|
|
"Skipping \"{}\" because it did not have an xmlUrl attribute.",
|
|
|
|
outline.text
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 22:06:15 +00:00
|
|
|
} else if args.json {
|
2020-08-28 11:37:29 +00:00
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
serde_json::to_string(&opml).expect("Failed to convert OPML to JSON")
|
|
|
|
);
|
2022-09-30 22:06:15 +00:00
|
|
|
} else if args.json_pretty {
|
2020-08-28 11:37:29 +00:00
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
serde_json::to_string_pretty(&opml)
|
|
|
|
.expect("Failed to convert OPML to pretty JSON")
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A helper function that takes in `opml::Outline` elements and returns all
|
|
|
|
/// children it can find in a single `Vec<Outline>`.
|
|
|
|
pub fn extract_all_outlines(outlines: &[Outline]) -> Vec<Outline> {
|
|
|
|
let mut accumulator = vec![];
|
|
|
|
|
|
|
|
for outline in outlines {
|
|
|
|
accumulator.push(outline.clone());
|
|
|
|
accumulator.append(&mut extract_all_outlines(&outline.outlines));
|
|
|
|
}
|
|
|
|
|
|
|
|
accumulator
|
|
|
|
}
|