Initial code commit

This commit is contained in:
Arne Keller 2018-10-18 20:24:18 +02:00
parent 1204db09be
commit 9ef881779c
5 changed files with 570 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "firefox-bookmarks"
version = "0.1.0"
authors = ["Arne <arne.keller01@yahoo.de>"]
edition = "2018"
[dependencies]
serde_json = "1.0.32"
chrono = { version = "0.4.6", features = ["serde"] }
if_chain = "0.1.3"

View File

@ -0,0 +1,438 @@
{
"guid": "root________",
"title": "",
"index": 0,
"dateAdded": 1491589624976000,
"lastModified": 1539777544394000,
"id": 1,
"typeCode": 2,
"type": "text/x-moz-place-container",
"root": "placesRoot",
"children": [
{
"guid": "menu________",
"title": "Bookmarks Menu",
"index": 0,
"dateAdded": 1491589624976000,
"lastModified": 1539777544390000,
"id": 2,
"typeCode": 2,
"type": "text/x-moz-place-container",
"root": "bookmarksMenuFolder",
"children": [
{
"guid": "Um2yJe5O1Ncq",
"title": "Kürzlich als Lesezeichen gesetzt",
"index": 0,
"dateAdded": 1491664220957000,
"lastModified": 1491664220994000,
"id": 1146,
"typeCode": 1,
"annos": [
{
"name": "Places/SmartBookmark",
"value": "RecentlyBookmarked",
"expires": 4,
"flags": 0
}
],
"type": "text/x-moz-place",
"uri": "place:parent=menu________&parent=unfiled_____&parent=toolbar_____&queryType=1&sort=12&maxResults=10&excludeQueries=1"
},
{
"guid": "791C5kUxH6qd",
"title": "Kürzlich verwendete Schlagwörter",
"index": 1,
"dateAdded": 1491664220306000,
"lastModified": 1491664220335000,
"id": 1131,
"typeCode": 1,
"annos": [
{
"name": "Places/SmartBookmark",
"value": "RecentTags",
"expires": 4,
"flags": 0
}
],
"type": "text/x-moz-place",
"uri": "place:type=6&sort=14&maxResults=10"
},
{
"guid": "5Q9Fz5osb5TE",
"title": "",
"index": 2,
"dateAdded": 1491664220287000,
"lastModified": 1491664220287000,
"id": 1130,
"typeCode": 3,
"type": "text/x-moz-place-separator"
},
{
"guid": "egZujOnfSOYo",
"title": "ASCII-Art",
"index": 3,
"dateAdded": 1491664168082000,
"lastModified": 1491664218958000,
"id": 73,
"typeCode": 2,
"type": "text/x-moz-place-container",
"children": [
{
"guid": "YHYnQP8BFI_a",
"title": ":joan stark's ASCII Art Gallery:",
"index": 0,
"dateAdded": 1491664205327000,
"lastModified": 1491664205327000,
"id": 823,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/index.htm"
},
{
"guid": "6YvIKzTZImqz",
"title": "ASCII Art by joan stark- index of new pics",
"index": 1,
"dateAdded": 1491664190619000,
"lastModified": 1491664190619000,
"id": 525,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexnew.htm"
},
{
"guid": "pPsIWfHa9BCU",
"title": "ASCII Art by joan stark- index of cartoons",
"index": 2,
"dateAdded": 1491664213570000,
"lastModified": 1491664213570000,
"id": 1021,
"typeCode": 1,
"iconuri": "https://sep.yimg.com/yf/common/1.0/ysb-logo-apple-touch-icon.png",
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexcartoon.htm"
},
{
"guid": "ixLhXcN9IBMO",
"title": "ASCII Art by joan stark- index of animals",
"index": 3,
"dateAdded": 1491664210698000,
"lastModified": 1491664210698000,
"id": 950,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexanimals.htm"
},
{
"guid": "QsrdznMz2tQ8",
"title": "ASCII Art by joan stark- index of seasonal/holiday ASCII art",
"index": 4,
"dateAdded": 1491664201619000,
"lastModified": 1491664201619000,
"id": 735,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexholiday.htm"
},
{
"guid": "iotyruwQA32m",
"title": "ASCII Art by joan stark- index of people, creatures, characters",
"index": 5,
"dateAdded": 1491664210522000,
"lastModified": 1491664210522000,
"id": 947,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexpeople.htm"
},
{
"guid": "SMJSuvsNwdFW",
"title": "ASCII Art by joan stark- index of other",
"index": 6,
"dateAdded": 1491664202370000,
"lastModified": 1491664202370000,
"id": 749,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexother.htm"
},
{
"guid": "JIQ9gROWlOu1",
"title": "ASCII Art by joan stark- index of colorized ASCII art",
"index": 7,
"dateAdded": 1491664197602000,
"lastModified": 1491664197602000,
"id": 639,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexcolor.htm"
},
{
"guid": "_XPW_uRJCFRk",
"title": "Animiert",
"index": 8,
"dateAdded": 1491664206326000,
"lastModified": 1491664206326000,
"id": 845,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/indexjava.htm"
},
{
"guid": "BZcdWLJZFhla",
"title": "2001",
"index": 9,
"dateAdded": 1491664166646000,
"lastModified": 1491664214295000,
"id": 45,
"typeCode": 2,
"type": "text/x-moz-place-container",
"children": [
{
"guid": "qRVUN8DYaK0r",
"title": "April-Juni 2001",
"index": 0,
"dateAdded": 1491664214295000,
"lastModified": 1491664214295000,
"id": 1037,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/new.htm"
},
{
"guid": "-6Rr_T1fiC-5",
"title": "Januar-März 2001",
"index": 1,
"dateAdded": 1491664186900000,
"lastModified": 1491664186900000,
"id": 438,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/01march.htm"
}
]
},
{
"guid": "XGCfAbJG-MzP",
"title": "2000",
"index": 10,
"dateAdded": 1491664167564000,
"lastModified": 1491664217109000,
"id": 62,
"typeCode": 2,
"type": "text/x-moz-place-container",
"children": [
{
"guid": "cjor022uaAqi",
"title": "September-Oktober 2000",
"index": 0,
"dateAdded": 1491664207591000,
"lastModified": 1491664207591000,
"id": 873,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/00oct.htm"
},
{
"guid": "tgjI6E6kkrIM",
"title": "November-Dezember 2000",
"index": 1,
"dateAdded": 1491664217109000,
"lastModified": 1491664217109000,
"id": 1069,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/00novdec.htm"
},
{
"guid": "aDKJxLrPi8mR",
"title": "April-August 2000",
"index": 2,
"dateAdded": 1491664206502000,
"lastModified": 1491664206502000,
"id": 849,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/00august.htm"
},
{
"guid": "KUvJi8STg0G_",
"title": "September 1999-März 2000",
"index": 3,
"dateAdded": 1491664198431000,
"lastModified": 1491664198431000,
"id": 658,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/00march.htm"
}
]
},
{
"guid": "uK22BYcA07E1",
"title": "1999",
"index": 11,
"dateAdded": 1491664170852000,
"lastModified": 1491664218958000,
"id": 103,
"typeCode": 2,
"type": "text/x-moz-place-container",
"children": [
{
"guid": "kkSBT9RxxN71",
"title": "September 1999-März 2000",
"index": 0,
"dateAdded": 1491664211565000,
"lastModified": 1491664211565000,
"id": 971,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/00march.htm"
},
{
"guid": "7L4HUg6an_0M",
"title": "Juli-August 1999",
"index": 1,
"dateAdded": 1491664191167000,
"lastModified": 1491664191167000,
"id": 536,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99julyaug.htm"
},
{
"guid": "ECS66XyyhPL5",
"title": "Juni 1999",
"index": 2,
"dateAdded": 1491664193867000,
"lastModified": 1491664193867000,
"id": 597,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99june.htm"
},
{
"guid": "XEZlOMdrPYCB",
"title": "Mai 1999",
"index": 3,
"dateAdded": 1491664204556000,
"lastModified": 1491664204556000,
"id": 803,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99may.htm"
},
{
"guid": "dypXQ3AyeRnT",
"title": "April 1999",
"index": 4,
"dateAdded": 1491664208411000,
"lastModified": 1491664208411000,
"id": 895,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99april.htm"
},
{
"guid": "-7R0xjRspq8U",
"title": "März 1999",
"index": 5,
"dateAdded": 1491664186950000,
"lastModified": 1491664186950000,
"id": 439,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99march.htm"
},
{
"guid": "xKNPTEjq8QJQ",
"title": "Februar 1999",
"index": 6,
"dateAdded": 1491664218958000,
"lastModified": 1491664218958000,
"id": 1100,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99feb.htm"
},
{
"guid": "ncZuWMmZf8MT",
"title": "Januar 1999",
"index": 7,
"dateAdded": 1491664212791000,
"lastModified": 1491664212791000,
"id": 1003,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/99jan.htm"
}
]
},
{
"guid": "7hIp0_DDUrKy",
"title": "1998",
"index": 12,
"dateAdded": 1491664165917000,
"lastModified": 1491664208760000,
"id": 38,
"typeCode": 2,
"type": "text/x-moz-place-container",
"children": [
{
"guid": "42AlHeurRDxQ",
"title": "Dezember 1998",
"index": 0,
"dateAdded": 1491664189354000,
"lastModified": 1491664189354000,
"id": 497,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/98dec.htm"
},
{
"guid": "61Ei_TpIaMa6",
"title": "November 1998",
"index": 1,
"dateAdded": 1491664190220000,
"lastModified": 1491664190220000,
"id": 516,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/98nov.htm"
},
{
"guid": "edmQQYvQwYxO",
"title": "September-Oktober 1998",
"index": 2,
"dateAdded": 1491664208760000,
"lastModified": 1491664208760000,
"id": 904,
"typeCode": 1,
"type": "text/x-moz-place",
"uri": "http://www.geocities.com/SoHo/7373/98sepoct.htm"
}
]
}
]
}]},
{
"guid": "mobile______",
"title": "Mobile Bookmarks",
"index": 1,
"dateAdded": 1491589624977000,
"lastModified": 1491589624977000,
"id": 6,
"typeCode": 2,
"annos": [
{
"name": "mobile/bookmarksRoot",
"value": "1",
"expires": 4,
"flags": 0
}
],
"type": "text/x-moz-place-container",
"root": "mobileFolder"
}
]
}

7
examples/read_file.rs Normal file
View File

@ -0,0 +1,7 @@
extern crate firefox_bookmarks;
use firefox_bookmarks::parse_json;
fn main() {
let data = parse_json(include_str!("bookmarks-sample.json")).unwrap();
println!("{:#?}", data);
}

112
src/lib.rs Normal file
View File

@ -0,0 +1,112 @@
#![recursion_limit = "512"]
extern crate serde_json;
use serde_json::Value;
extern crate chrono;
use chrono::NaiveDateTime;
#[macro_use] extern crate if_chain;
/// Either container or place
#[derive(Debug)]
pub enum Entity {
Container(PlaceContainer),
Place(Place)
}
/// x-moz-place-container
#[derive(Debug)]
pub struct PlaceContainer {
pub guid: String,
pub title: String,
pub index: u64,
pub date_added: NaiveDateTime,
pub last_modified: NaiveDateTime,
pub id: u64,
pub root: Option<String>,
pub children: Vec<Entity>
}
/// x-moz-place
#[derive(Debug)]
pub struct Place {
pub guid: String,
pub title: String,
pub index: u64,
pub date_added: NaiveDateTime,
pub last_modified: NaiveDateTime,
pub id: u64,
pub annos: Option<Value>,
pub uri: String
}
/// Only `Place`s and `PlaceContainer`s will be extracted, anything else is silently discarded.
pub fn parse_json(json: &str) -> Option<PlaceContainer> {
get_container(&serde_json::from_str(json).ok()?)
}
fn get_container(json: &Value) -> Option<PlaceContainer> {
if_chain! {
if Some(Some("text/x-moz-place-container")) == json.get("type").map(|x| x.as_str());
if let Some(Some(guid)) = json.get("guid").map(|x| x.as_str().map(|x| x.to_owned()));
if let Some(Some(title)) = json.get("title").map(|x| x.as_str().map(|x| x.to_owned()));
if let Some(Some(index)) = json.get("index").map(|x| x.as_u64());
if let Some(Some(date_added)) = json.get("dateAdded").map(|x| x.as_i64());
if let Some(date_added) = NaiveDateTime::from_timestamp_opt(date_added / 1_000_000, (date_added % 1_000_000) as u32 * 1000);
if let Some(Some(last_modified)) = json.get("lastModified").map(|x| x.as_i64());
if let Some(last_modified) = NaiveDateTime::from_timestamp_opt(last_modified / 1_000_000, (last_modified % 1_000_000) as u32 * 1000);
if let Some(Some(id)) = json.get("id").map(|x| x.as_u64());
if let root = json.get("root").map(|x| x.as_str()).and_then(|x| x.map(|x| x.to_owned()));
if let Some(children) = {
if let Some(Some(children)) = json.get("children").map(|x| x.as_array()) {
let mut children_parsed = Vec::new();
for c in children.into_iter() {
if let Some(x) = get_entity(c) {
children_parsed.push(x);
} else {
// too bad
}
}
Some(children_parsed)
} else {
Some(vec![])
}
};
then {
Some(PlaceContainer {
guid, title, index, date_added, last_modified, id, children, root
})
} else {
None
}
}
}
fn get_entity(json: &Value) -> Option<Entity> {
match json.get("type").map(|x| x.as_str()) {
Some(Some("text/x-moz-place")) => Some(Entity::Place(get_place(json)?)),
Some(Some("text/x-moz-place-container")) => Some(Entity::Container(get_container(json)?)),
_ => None
}
}
fn get_place(json: &Value) -> Option<Place> {
if_chain! {
if Some(Some("text/x-moz-place")) == json.get("type").map(|x| x.as_str());
if let Some(Some(guid)) = json.get("guid").map(|x| x.as_str().map(|x| x.to_owned()));
if let Some(Some(title)) = json.get("title").map(|x| x.as_str().map(|x| x.to_owned()));
if let Some(Some(index)) = json.get("index").map(|x| x.as_u64());
if let Some(Some(date_added)) = json.get("dateAdded").map(|x| x.as_i64());
if let Some(date_added) = NaiveDateTime::from_timestamp_opt(date_added / 1_000_000, (date_added % 1_000_000) as u32 * 1000);
if let Some(Some(last_modified)) = json.get("lastModified").map(|x| x.as_i64());
if let Some(last_modified) = NaiveDateTime::from_timestamp_opt(last_modified / 1_000_000, (last_modified % 1_000_000) as u32 * 1000);
if let Some(Some(id)) = json.get("id").map(|x| x.as_u64());
if let annos = json.get("annos").cloned();
if let Some(Some(uri)) = json.get("uri").map(|x| x.as_str().map(|x| x.to_owned()));
then {
Some(Place {
guid, title, index, date_added, last_modified, id, annos, uri
})
} else {
None
}
}
}