rustdoc-stripper-0.1.9/.gitignore010064400007650000024000000000071313077533500152650ustar0000000000000000target rustdoc-stripper-0.1.9/.travis.yml010064400007650000024000000002141313077533500154060ustar0000000000000000language: rust rust: - nightly - beta - stable script: - rustc --version - RUST_BACKTRACE=1 cargo build - RUST_BACKTRACE=1 cargo test rustdoc-stripper-0.1.9/Cargo.toml.orig010064400007650000024000000010631350423110300161500ustar0000000000000000[package] authors = ["Guillaume Gomez "] name = "rustdoc-stripper" version = "0.1.9" description = "A tool to manipulate rustdoc comments" repository = "https://github.com/GuillaumeGomez/rustdoc-stripper" homepage = "https://github.com/GuillaumeGomez/rustdoc-stripper" documentation = "https://github.com/GuillaumeGomez/rustdoc-stripper" license = "Apache-2.0" readme = "README.md" keywords = ["rustdoc", "doc", "strip", "tool"] [[bin]] name = "rustdoc-stripper" [dev-dependencies] tempfile = "3.0.5" [lib] name = "stripper_lib" rustdoc-stripper-0.1.9/Cargo.toml0000644000000021040000000000000124260ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "rustdoc-stripper" version = "0.1.9" authors = ["Guillaume Gomez "] description = "A tool to manipulate rustdoc comments" homepage = "https://github.com/GuillaumeGomez/rustdoc-stripper" documentation = "https://github.com/GuillaumeGomez/rustdoc-stripper" readme = "README.md" keywords = ["rustdoc", "doc", "strip", "tool"] license = "Apache-2.0" repository = "https://github.com/GuillaumeGomez/rustdoc-stripper" [lib] name = "stripper_lib" [[bin]] name = "rustdoc-stripper" [dev-dependencies.tempfile] version = "3.0.5" rustdoc-stripper-0.1.9/Cargo.toml.orig0000644000000010630000000000000133700ustar00[package] authors = ["Guillaume Gomez "] name = "rustdoc-stripper" version = "0.1.9" description = "A tool to manipulate rustdoc comments" repository = "https://github.com/GuillaumeGomez/rustdoc-stripper" homepage = "https://github.com/GuillaumeGomez/rustdoc-stripper" documentation = "https://github.com/GuillaumeGomez/rustdoc-stripper" license = "Apache-2.0" readme = "README.md" keywords = ["rustdoc", "doc", "strip", "tool"] [[bin]] name = "rustdoc-stripper" [dev-dependencies] tempfile = "3.0.5" [lib] name = "stripper_lib" rustdoc-stripper-0.1.9/LICENSE010064400007650000024000000261221313077533500143100ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2015 Gomez Guillaume Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rustdoc-stripper-0.1.9/README.md010064400007650000024000000026521313077533500145640ustar0000000000000000# rustdoc-stripper [![Build Status](https://api.travis-ci.org/GuillaumeGomez/rustdoc-stripper.png?branch=master)](https://travis-ci.org/GuillaumeGomez/rustdoc-stripper) `rustdoc-stripper` is a tool used to remove rustdoc comments from your code and save them in a `comments.cmts` file if you want to regenerate them. ###Options Available options for rustdoc-stripper are: * -h | --help : Displays this help * -s | --strip : Strips the current folder files and create a file with rustdoc information (comments.cmts by default) * -g | --regenerate : Recreate files with rustdoc comments from reading rustdoc information file (comments.cmts by default) * -n | --no-file-output : Display rustdoc information directly on stdout * -i | --ignore [filename]: Ignore the specified file, can be repeated as much as needed, only used when stripping files, ignored otherwise * -d | --dir [directory] : Specify a directory path to work on, optional * -v | --verbose : Activate verbose mode * -f | --force : Remove confirmation demands * -m | --ignore-macros : macros in hierarchy will be ignored (so only macros with doc comments will appear in the comments file) * -o | --comment-file : specify the file within you want to save rustdoc information By default, rustdoc is run with -s option: ```Shell ./rustdoc-stripper -s ``` IMPORTANT: Only files ending with '.rs' will be stripped/regenerated. rustdoc-stripper-0.1.9/src/consts.rs010064400007650000024000000015241313077533500157500ustar0000000000000000// Copyright 2016 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. pub const MOD_COMMENT : &'static str = ""; pub const OUTPUT_COMMENT_FILE : &'static str = "comments.md"; rustdoc-stripper-0.1.9/src/lib.rs010064400007650000024000000021151313077533500152020ustar0000000000000000// Copyright 2015 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. pub mod regenerate; pub mod strip; pub mod types; pub mod utils; mod consts; pub use regenerate::{ parse_cmts, regenerate_comments, regenerate_doc_comments, }; pub use strip::strip_comments; pub use consts::{ FILE, FILE_COMMENT, MOD_COMMENT, END_INFO, OUTPUT_COMMENT_FILE, }; pub use types::{ EventType, Type, TypeStruct, }; pub use utils::{ write_comment, write_item_doc, write_file, write_file_name, write_file_comment, loop_over_files, };rustdoc-stripper-0.1.9/src/main.rs010064400007650000024000000235561313077626700154010ustar0000000000000000// Copyright 2015 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. extern crate stripper_lib; use std::{env, io}; use std::io::{BufRead, Write}; use std::path::Path; use std::fs::File; use stripper_lib::regenerate::regenerate_doc_comments; use stripper_lib::strip_comments; use stripper_lib::OUTPUT_COMMENT_FILE; use stripper_lib::loop_over_files; struct ExecOptions { stdout_output: bool, strip: bool, regenerate: bool, ignore_macros: bool, ignore_doc_commented: bool, } fn check_options(args: &mut ExecOptions, to_change: char) -> bool { if to_change == 's' { args.strip = true; } else { args.regenerate = true; } if args.regenerate && args.strip { println!("You cannot strip and regenerate at the same time!"); println!("Rerun with -h option for more information"); false } else { true } } fn print_help() { println!(r#"Available options for rustdoc-stripper: -h | --help : Displays this help -s | --strip : Strips the specified folder's files and create a file with doc comments (comments.md by default) -g | --regenerate : Recreate files with doc comments from reading doc comments file (comments.md by default) -n | --no-file-output : Display doc comments directly on stdout -i | --ignore [filename] : Ignore the specified file, can be repeated as much as needed, only used when stripping files, ignored otherwise -d | --dir [directory] : Specify a directory path to work on, optional -v | --verbose : Activate verbose mode -f | --force : Remove confirmation demands -m | --ignore-macros : Macros in hierarchy will be ignored (so only macros with doc comments will appear in the comments file) -o | --comment-file : Specify the file where you want to save/load doc comments -x | --ignore-doc-commented: When regenerating doc comments, if doc comments are already present, stored doc comment won't be regenerated By default, rustdoc-stripper is run with -s option: ./rustdoc-stripper -s IMPORTANT: Only files ending with '.rs' will be stripped/regenerated."#); } fn ask_confirmation(out_file: &str) -> bool { let r = io::stdin(); let mut reader = r.lock(); let mut line = String::new(); let stdout = io::stdout(); let mut stdo = stdout.lock(); print!(r##"A file '{}' already exists. If you want to run rustdoc-stripper anyway, it'll erase the file and its data. Which means that if your files don't have rustdoc comments anymore, you'll loose them. Do you want to continue ? (y/n) "##, out_file); let _ = stdo.flush(); match reader.read_line(&mut line) { Ok(_) => { line = line.trim().to_owned(); if line != "y" && line != "Y" { if line == "n" || line == "N" { println!("Aborting..."); } else { println!("Unknown answer: '{}'.\nAborting...", line); } false } else { true } } Err(e) => { println!("An error occured: {}.\nAborting...", e); false } } } fn main() { let mut args = ExecOptions { stdout_output: false, strip: false, regenerate: false, ignore_macros: false, ignore_doc_commented: false, }; let mut first = true; let mut wait_filename = false; let mut wait_directory = false; let mut files_to_ignore = vec!(); let mut directory = ".".to_owned(); let mut verbose = false; let mut force = false; let mut wait_out_file = false; let mut out_file = OUTPUT_COMMENT_FILE.to_owned(); for argument in env::args() { if first { first = false; continue; } if wait_filename { files_to_ignore.push(argument.clone()); wait_filename = false; continue; } if wait_directory { directory = argument.clone(); wait_directory = false; continue; } if wait_out_file { out_file = argument.clone(); wait_out_file = false; continue; } match &*argument { "-h" | "--help" => { print_help(); return; } "-s" | "--strip" => { if !check_options(&mut args, 's') { return; } } "-i" | "--ignore" => { wait_filename = true; } "-d" | "--dir" => { wait_directory = true; } "-o" | "--comment-file" => { wait_out_file = true; } "-g" | "--regenerate" => { if !check_options(&mut args, 'g') { return; } } "-n" | "--no-file-output" => { args.stdout_output = true; } "-v" | "--verbose" => { verbose = true; } "-f" | "--force" => { force = true; } "-m" | "--ignore-macros" => { args.ignore_macros = true; } "-x" | "--ignore-doc-commented" => { args.ignore_doc_commented = true; } "-" | "--" => { println!("Unknown option: '-'"); return; } s => { if s.chars().next().unwrap() != '-' { println!("Unknown option: '{}'", s); return; } for c in (&s[1..]).chars() { match c { 's' | 'g' => { if !check_options(&mut args, c) { return; } } 'n' => { args.stdout_output = true; } 'm' => { args.ignore_macros = true; } 'x' => { args.ignore_doc_commented = true; } 'h' => { print_help(); return; } 'v' => { verbose = true; } 'f' => { force = true; } err if err == 'i' || err == 'd' => { println!("'{}' have to be used separately from other options. Example:", err); println!("./rustdoc-stripper -s -{} foo", err); return; } err => { println!("Unknown option: {}", err); return; } } } } } } if wait_filename { println!("[-i | --ignore] option expects a filename. Example:"); println!("./rustdoc-stripper -i src/foo.rs"); return; } if wait_directory { println!("[-d | --dir] option expects a directory path. Example:"); println!("./rustdoc-stripper -d src/"); return; } if wait_out_file { println!("[-o | --comment-file] option expects a file path. Example:"); println!("./rustdoc-stripper -o src/out.md"); return; } if args.strip == true || (args.strip == false && args.regenerate == false) { let comments_path = Path::new(&out_file); if comments_path.exists() { if comments_path.is_file() { if !force && !ask_confirmation(&out_file) { return; } } else { println!("An element called '{}' already exists. Aborting...", &out_file); return; } } println!("Starting stripping..."); if args.stdout_output { let stdout = io::stdout(); let mut stdout = stdout.lock(); loop_over_files(directory.as_ref(), &mut move |w, s| { strip_comments(w, s, &mut stdout, args.ignore_macros) }, &files_to_ignore, verbose); } else { match File::create(&out_file) { Ok(mut f) => { loop_over_files(directory.as_ref(), &mut |w, s| { strip_comments(w, s, &mut f, args.ignore_macros) }, &files_to_ignore, verbose); } Err(e) => { println!("Error while opening \"{}\": {}", &out_file, e); return; } } } } else { println!("Starting regeneration..."); regenerate_doc_comments(&directory, verbose, &out_file, args.ignore_macros, args.ignore_doc_commented); } println!("Done !"); } rustdoc-stripper-0.1.9/src/regenerate.rs010064400007650000024000000425561350423107500165630ustar0000000000000000// Copyright 2015 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::fs::{File, OpenOptions, remove_file}; use std::io::{BufRead, BufReader, Write}; use std::collections::HashMap; use std::ops::Deref; use std::path::Path; use strip; use types::{EventType, ParseResult, Type, TypeStruct}; use utils::{join, loop_over_files, write_comment, write_file}; use std::iter; use consts::{ MOD_COMMENT, FILE_COMMENT, FILE, END_INFO, }; fn gen_indent(indent: usize) -> String { iter::repeat(" ").take(indent).collect::>().join("") } fn gen_indent_from(from: &str) -> String { for (i, c) in from.chars().enumerate() { if c != ' ' && c != '\t' { return gen_indent(i / 4); } } String::new() } fn regenerate_comment(is_file_comment: bool, position: usize, indent: usize, comment: &str, original_content: &mut Vec) { let is_empty = comment.trim().is_empty(); let read_indent = if is_file_comment { gen_indent(indent) } else { let tmp = original_content[position].clone(); gen_indent_from(&tmp) }; original_content.insert(position, format!("{}{}{}{}", &read_indent, if is_file_comment { "//!" } else { "///" }, if is_empty { "" } else { " " }, if is_empty { "" } else { &comment })); } fn get_corresponding_type(elements: &[(Option, Vec)], to_find: &Option, mut line: usize, decal: &mut usize, original_content: &mut Vec, ignore_macros: bool) -> Option { let mut pos = 0; while pos < elements.len() { if match (&elements[pos].0, to_find) { (&Some(ref a), &Some(ref b)) => { let ret = a == b; // to detect variants if !ret && b.ty == Type::Unknown && b.parent.is_some() && a.parent.is_some() && a.parent == b.parent { if match b.parent { Some(ref p) => { p.ty == Type::Struct || p.ty == Type::Enum || p.ty == Type::Use } None => false, } { let mut tmp = b.clone(); tmp.ty = Type::Variant; a == &tmp } else { false } } else { ret } }, _ => false, } { let mut file_comment = false; if elements[pos].1.len() > 0 && elements[pos].1[0].starts_with("//!") { line += 1; file_comment = true; } else { while line > 0 && (line + *decal) > 0 && original_content[line + *decal - 1].trim_start().starts_with("#") { line -= 1; } } for comment in (&elements[pos].1).into_iter().skip(if file_comment { 1 } else { 0 }) { let depth = if let Some(ref e) = elements[pos].0 { e.get_depth(ignore_macros) } else { 0 }; regenerate_comment(file_comment, line + *decal, depth + 1, &comment, original_content); *decal += 1; } return Some(pos); } pos += 1; } None } // The hashmap key is `Some(file name)` or `None` for entries that ignore file name pub fn regenerate_comments(work_dir: &Path, path: &str, infos: &mut HashMap, Vec<(Option, Vec)>>, ignore_macros: bool, ignore_doc_commented: bool) { if !infos.contains_key(&None) && !infos.contains_key(&Some(path.to_owned())) { return; } let full_path = work_dir.join(path); match strip::build_event_list(&full_path) { Ok(ref mut parse_result) => { // exact path match if let Some(v) = infos.get_mut(&Some(path.to_owned())) { do_regenerate(&full_path, parse_result, v, ignore_macros, ignore_doc_commented); } // apply to all files if let Some(v) = infos.get_mut(&None) { do_regenerate(&full_path, parse_result, v, ignore_macros, ignore_doc_commented); } } Err(e) => { println!("Error in file '{}': {}", path, e); } } } fn check_if_regen(it: usize, parse_result: &ParseResult, ignore_doc_commented: bool) -> bool { ignore_doc_commented && it > 0 && match parse_result.event_list[it - 1].event { EventType::Comment(_) | EventType::FileComment(_) => true, _ => false, } } fn do_regenerate(path: &Path, parse_result: &mut ParseResult, elements: &mut Vec<(Option, Vec)>, ignore_macros: bool, ignore_doc_commented: bool) { let mut position = 0; let mut decal = 0; // first, we need to put back file comment for entry in elements.iter() { if entry.0.is_none() { let mut it = 0; while it < parse_result.original_content.len() && parse_result.original_content[it].starts_with("/") { it += 1; } if it > 0 { it += 1; } if it < parse_result.original_content.len() { for line in &entry.1 { if line.trim().is_empty() { parse_result.original_content.insert(it, format!("//!")); } else { parse_result.original_content.insert(it, format!("//! {}", &line)); } decal += 1; it += 1; } } parse_result.original_content.insert(it, "".to_owned()); decal += 1; break; } position += 1; } if position < elements.len() { elements.remove(position); } let mut waiting_type = None; let mut current = None; let mut it = 0; while it < parse_result.event_list.len() { match parse_result.event_list[it].event { EventType::Type(ref t) => { if t.ty != Type::Unknown { waiting_type = Some(t.clone()); let tmp = { let t = strip::add_to_type_scope(¤t, &waiting_type); if ignore_macros { erase_macro_path(t) } else { t } }; if !check_if_regen(it, parse_result, ignore_doc_commented) { match get_corresponding_type(&elements, &tmp, parse_result.event_list[it].line, &mut decal, &mut parse_result.original_content, ignore_macros) { Some(l) => { elements.remove(l); }, None => {} } } } else { match current { Some(ref c) => { if c.ty == Type::Struct || c.ty == Type::Enum || c.ty == Type::Mod { let tmp = Some(t.clone()); let cc = { let t = strip::add_to_type_scope(¤t, &tmp); if ignore_macros { erase_macro_path(t) } else { t } }; if !check_if_regen(it, parse_result, ignore_doc_commented) { match get_corresponding_type(&elements, &cc, parse_result.event_list[it].line, &mut decal, &mut parse_result.original_content, ignore_macros) { Some(l) => { elements.remove(l); } None => {} } } } } None => {} } } } EventType::InScope => { current = strip::add_to_type_scope(¤t, &waiting_type); waiting_type = None; } EventType::OutScope => { current = strip::type_out_scope(¤t); waiting_type = None; } _ => {} } it += 1; } rewrite_file(path, &parse_result.original_content); } fn rewrite_file(path: &Path, o_content: &[String]) { match File::create(path) { Ok(mut f) => { write!(f, "{}", o_content.join("\n")).unwrap(); } Err(e) => { println!("Cannot open '{}': {}", path.display(), e); } } } fn parse_mod_line(line: &str) -> Option { let line = line.replace(FILE_COMMENT, "").replace(MOD_COMMENT, "").replace(END_INFO, ""); if line.len() < 1 { return None } let parts : Vec<&str> = line.split("::").collect(); let mut current = None; for part in parts { let elems : Vec<&str> = part.split(" ").filter(|x| x.len() > 0).collect(); current = strip::add_to_type_scope(¤t.clone(), &Some(TypeStruct::new(Type::from(elems[0]), elems[elems.len() - 1]))); } current } fn save_remainings(infos: &HashMap, Vec<(Option, Vec)>>, comment_file: &str) { let mut remainings = 0; for (_, content) in infos { if content.len() > 0 { remainings += 1; } } if remainings < 1 { let _ = remove_file(comment_file); return; } match File::create(comment_file) { Ok(mut out_file) => { println!("Some comments haven't been regenerated to the files. Saving them \ back to '{}'.", comment_file); for (key, content) in infos { if content.len() < 1 { continue; } // Set the name to "*" for entries that ignore file name let key = key.as_ref().map(|s| &s[..]).unwrap_or("*"); let _ = writeln!(out_file, "{}", &write_file(key)); for line in content { match line.0 { Some(ref d) => { let _ = writeln!(out_file, "{}", write_comment(d, &join(&line.1, "\n"), false)); } None => {} } } } }, Err(e) => { println!("An error occured while trying to open '{}': {}", comment_file, e); return; }, } } pub fn regenerate_doc_comments(directory: &str, verbose: bool, comment_file: &str, ignore_macros: bool, ignore_doc_commented: bool) { // we start by storing files info let f = match OpenOptions::new().read(true).open(comment_file) { Ok(f) => f, Err(e) => { println!("An error occured while trying to open '{}': {}", comment_file, e); return; }, }; let reader = BufReader::new(f); let lines = reader.lines().map(|line| line.unwrap()); let mut infos = parse_cmts(lines, ignore_macros); let ignores: &[&str] = &[]; loop_over_files(directory.as_ref(), &mut |w, s| { regenerate_comments(w, s, &mut infos, ignore_macros, ignore_doc_commented) }, &ignores, verbose); save_remainings(&infos, comment_file); } fn sub_erase_macro_path(ty: Option>, is_parent: bool) -> Option> { match ty { Some(ref t) if is_parent => { if t.ty == Type::Macro { sub_erase_macro_path(t.clone().parent, true) } else { let mut tmp = t.clone(); tmp.parent = sub_erase_macro_path(t.clone().parent, true); Some(tmp) } } Some(t) => { let mut tmp = t.clone(); tmp.parent = sub_erase_macro_path(t.parent, true); Some(tmp) } None => None, } } fn erase_macro_path(ty: Option) -> Option { if let Some(t) = ty { Some(*sub_erase_macro_path(Some(Box::new(t)), false).unwrap()) } else { None } } pub fn parse_cmts(mut lines: I, ignore_macros: bool) -> HashMap, Vec<(Option, Vec)>> where S: Deref, I: Iterator { enum State { Initial, File { file: Option, infos: Vec<(Option, Vec)>, ty: Option, comments: Vec, } } // Returns `Some(name)` if the line matches FILE // where name is Some for an actual file name and None for "*" // The "*" entries are to be applied regardless of file name fn line_file(line: &str) -> Option> { if line.starts_with(FILE) { let name = &line[FILE.len()..].replace(END_INFO, ""); if name == "*" { Some(None) } else { Some(Some(name.to_owned())) } } else { None } } let mut ret = HashMap::new(); let mut state = State::Initial; while let Some(line) = lines.next() { state = match state { State::Initial => { if let Some(file) = line_file(&line) { State::File { file: file, infos: vec![], ty: None, comments: vec![], } } else { panic!("Unrecognized format"); } }, State::File { mut file, mut infos, mut ty, mut comments } => { if let Some(new_file) = line_file(&line) { if !comments.is_empty() { infos.push((ty.take(), comments)); comments = vec![]; } if !infos.is_empty() { ret.insert(file, infos); file = new_file; infos = vec![]; } } else if line.starts_with(FILE_COMMENT) { if let Some(ty) = ty.take() { if !comments.is_empty() { infos.push((Some(ty), comments)); comments = vec!["//!".to_owned()]; } } else { if !comments.is_empty() { infos.push((None, comments)); comments = vec![]; } } ty = parse_mod_line(&line[..]); } else if line.starts_with(MOD_COMMENT) { if !comments.is_empty() { infos.push((ty, comments)); comments = vec![]; } ty = parse_mod_line(&line[..]); } else { comments.push(line[..].to_owned()); } State::File { file, infos, ty: if ignore_macros { erase_macro_path(ty) } else { ty }, comments, } }, } } if let State::File { file, mut infos, ty, comments } = state { if !comments.is_empty() { infos.push((ty, comments)); } if !infos.is_empty() { ret.insert(file, infos); } } ret } rustdoc-stripper-0.1.9/src/strip.rs010064400007650000024000000560401350423107500155740ustar0000000000000000// Copyright 2015 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::fs::File; use std::io::{self, Write, Read}; use std::path::Path; use std::process::exit; use std::ops::Deref; use utils::{join, write_comment, write_file, write_file_comment}; use types::{EventInfo, EventType, ParseResult, Type, TypeStruct}; const STOP_CHARACTERS : &'static [char] = &['\t', '\n', '\r', '<', '{', ':', ';', '!', '(']; const COMMENT_ID : &'static [&'static str] = &["//", "/*"]; const DOC_COMMENT_ID : &'static [&'static str] = &["///", "/*!", "//!"]; fn move_to(words: &[&str], it: &mut usize, limit: &str, line: &mut usize, start_remove: &str) { if words[*it][start_remove.len()..].contains(&limit) { return; } *it += 1; while *it < words.len() && words[*it].contains(limit) == false { if words[*it] == "\n" { *line += 1; } *it += 1; } if *it < words.len() && words[*it] == "\n" { *line += 1; } } fn move_until(words: &[&str], it: &mut usize, limit: &str, line: &mut usize) { let alternative1 = format!("{};", limit); let alternative2 = format!("{}\n", limit); while *it < words.len() && !words[*it].ends_with(limit) && !words[*it].ends_with(&alternative1) && !words[*it].ends_with(&alternative2) { *line += words[*it].chars().filter(|c| *c == '\n').count(); *it += 1; } } fn get_before<'a>(word: &'a str, limits: &[char]) -> &'a str { word.find(limits).map(|pos| &word[..pos]).unwrap_or(word) } fn get_impl(words: &[&str], it: &mut usize, line: &mut usize) -> Vec { let mut v = vec!(); while *it + 1 < words.len() { if words[*it] == "\n" { *line += 1; } if words[*it + 1] == "{" || words[*it + 1] == ";" { break; } *it += 1; v.push(words[*it].to_owned()); } v } pub fn add_to_type_scope(current: &Option, e: &Option) -> Option { match current { &Some(ref c) => { match e { &Some(ref t) => { let mut tmp = t.clone(); tmp.parent = Some(Box::new(c.clone())); Some(tmp) } _ => { let mut tmp = TypeStruct::empty(); tmp.parent = Some(Box::new(c.clone())); Some(tmp) } } }, &None => match e { &Some(ref t) => Some(t.clone()), _ => None, } } } pub fn type_out_scope(current: &Option) -> Option { match current { &Some(ref c) => match c.parent { Some(ref p) => Some(p.deref().clone()), None => None, }, &None => None, } } fn get_mod(current: &Option) -> bool { match *current { Some(ref t) => { if t.ty != Type::Mod { println!("Mod/File comments cannot be put here!"); false } else { true } } None => true, } } enum BlockKind<'a> { Comment((String, String, &'a str)), DocComment((String, String, &'a str)), Other(&'a str), } fn get_three_parts<'a>(before: &'a str, comment_sign: &str, after: &'a str, stop: &str) -> (String, String, &'a str) { if let Some(pos) = after.find(stop) { (before.to_owned(), format!("{} {}", comment_sign, &after[0..pos]), &after[pos..]) } else { (before.to_owned(), format!("{} {}", comment_sign, &after), &after[after.len() - 1..]) } } fn find_one_of<'a>(comments: &[&str], doc_comments: &[&str], text: &'a str) -> BlockKind<'a> { let mut last_pos = 0; loop { let tmp_text = &text[last_pos..]; if let Some(pos) = tmp_text.find('/') { let tmp_text = &tmp_text[pos..]; last_pos = pos + last_pos; for com in doc_comments { if tmp_text.starts_with(com) { if &com[1..2] == "*" { return BlockKind::DocComment(get_three_parts(&text[0..last_pos], com, &text[last_pos + com.len()..], "*/")) } else { return BlockKind::DocComment(get_three_parts(&text[0..last_pos], com, &text[last_pos + com.len()..], "\n")) } } } for com in comments { if tmp_text.starts_with(com) { if &com[1..2] == "*" { return BlockKind::Comment(get_three_parts(&text[0..last_pos], "", &text[last_pos..], "*/")) } else { return BlockKind::Comment(get_three_parts(&text[0..last_pos], "", &text[last_pos..], "\n")) } } } } return BlockKind::Other(text) } } fn transform_code(code: &str) -> String { code.replace("{", " { ") .replace("}", " } ") .replace(":", " : ") .replace(" : : ", "::") .replace("*/", " */") .replace("\n", " \n ") .replace("!(", " !! (") .replace("! {", " !? {") .replace(",", ", ") .replace("(", " (") } fn clean_input(mut s: &str) -> String { let mut ret = String::new(); loop { s = match find_one_of(COMMENT_ID, DOC_COMMENT_ID, s) { BlockKind::Comment((s, comment, after)) => { ret.push_str(&transform_code(&s)); for _ in 0..comment.split("\n").count() - 1 { ret.push_str(" \n "); } after } BlockKind::DocComment((s, doc_comment, after)) => { ret.push_str(&transform_code(&s)); ret.push_str(&doc_comment); after } BlockKind::Other(s) => { ret.push_str(&transform_code(s)); return ret } }; } } fn clear_events(mut events: Vec) -> Vec { let mut current : Option = None; let mut waiting_type : Option = None; let mut it = 0; while it < events.len() { if match events[it].event { EventType::Type(ref t) => { if t.ty != Type::Unknown { waiting_type = Some(t.clone()); false } else { if let Some(ref parent) = current { match parent.ty { Type::Struct | Type::Enum => false, _ => true, } } else { true } } } EventType::InScope => { current = add_to_type_scope(¤t, &waiting_type); waiting_type = None; false } EventType::OutScope => { current = type_out_scope(¤t); waiting_type = None; false } _ => false, } { events.remove(it); continue } it += 1; } events } fn build_event_inner( it: &mut usize, line: &mut usize, words: &[&str], event_list: &mut Vec, comment_lines: &mut Vec, b_content: &[String], mut par_count: Option, ) { let mut waiting_for_macro = false; while *it < words.len() { match words[*it] { c if c.starts_with("\"") => move_to(&words, it, "\"", line, "\""), c if c.starts_with("b\"") => move_to(&words, it, "\"", line, "b\""), // c if c.starts_with("'") => move_to(&words, it, "'", line), c if c.starts_with("r#") => { let end = c.split("#\"").next().unwrap().replace("\"", "").replace("r", ""); move_to(&words, it, &format!("\"{}", end), line, "r#"); } "///" => { comment_lines.push(*line); event_list.push( EventInfo::new(*line, EventType::Comment(b_content[*line].to_owned()))); move_to(&words, it, "\n", line, ""); } "//!" => { comment_lines.push(*line); event_list.push( EventInfo::new(*line, EventType::FileComment(b_content[*line].to_owned()))); if *line + 1 < b_content.len() && b_content[*line + 1].len() < 1 { comment_lines.push(*line + 1); } move_to(&words, it, "\n", line, ""); } "/*!" => { let mark = *line; move_until(&words, it, "*/", line); for pos in mark..*line { comment_lines.push(pos); event_list.push( EventInfo::new(*line, EventType::FileComment(b_content[pos].to_owned()))); } comment_lines.push(*line); let mut removed = false; if *line + 1 < b_content.len() && b_content[*line + 1].len() < 1 { comment_lines.push(*line + 1); removed = true; } event_list.push( EventInfo::new(*line, EventType::FileComment("*/".to_owned()))); if removed { event_list.push( EventInfo::new(*line, EventType::FileComment("".to_owned()))); } } "use" | "mod" => { let mut name = words[*it + 1].to_owned(); let ty = words[*it]; if *line + 1 < b_content.len() && b_content[*line].ends_with("::{") { move_to(&words, it, "\n", line, ""); name.push_str(&format!("{}", b_content[*line + 1].trim())); } event_list.push( EventInfo::new(*line, EventType::Type(TypeStruct::new(Type::from(ty), &name)))); } "struct" | "fn" | "enum" | "const" | "static" | "type" | "trait" | "macro_rules!" | "flags" => { if *it + 1 >= words.len() { break } event_list.push( EventInfo::new(*line, EventType::Type( TypeStruct::new( Type::from(words[*it]), get_before(words[*it + 1], STOP_CHARACTERS))))); waiting_for_macro = words[*it] == "macro_rules!"; *it += 1; } "!!" => { event_list.push(EventInfo::new(*line, EventType::Type(TypeStruct::new(Type::from("macro"), &format!("{}!{}", words[*it - 1], words[*it + 1]))))); *it += 1; } "!?" => { event_list.push(EventInfo::new(*line, EventType::Type(TypeStruct::new(Type::from("macro"), &format!("{}!", words[*it - 1]))))); } "impl" => { event_list.push( EventInfo::new(*line, EventType::Type( TypeStruct::new( Type::Impl, &join(&get_impl(&words, it, line), " "))))); } "{" => { if let Some(ref mut par_count) = par_count { *par_count += 1; } event_list.push(EventInfo::new(*line, EventType::InScope)); if waiting_for_macro { build_event_inner( it, line, &words, &mut vec![], &mut vec![], &b_content, Some(1), ); waiting_for_macro = false; } } "}" => { if let Some(ref mut par_count) = par_count { *par_count -= 1; if *par_count <= 0 { return } } event_list.push(EventInfo::new(*line, EventType::OutScope)); } "\n" => { *line += 1; } s if s.starts_with("#[") || s.starts_with("#![") => { while *it < words.len() { *line += words[*it].split("\n").count() - 1; if words[*it].contains("]") { break; } *it += 1; } } _ => { event_list.push( EventInfo::new(*line, EventType::Type(TypeStruct::new(Type::Unknown, words[*it])))); } } *it += 1; } } pub fn build_event_list(path: &Path) -> io::Result { let mut f = File::open(path)?; let mut b_content = String::new(); f.read_to_string(&mut b_content).unwrap(); let content = clean_input(&b_content); let b_content: Vec = b_content.split('\n').map(|s| s.to_owned()).collect(); let words: Vec<&str> = content.split(' ').filter(|s| s.len() > 0).collect(); let mut it = 0; let mut line = 0; let mut event_list = vec!(); let mut comment_lines = vec!(); build_event_inner( &mut it, &mut line, &words, &mut event_list, &mut comment_lines, &b_content, None, ); Ok(ParseResult { event_list: clear_events(event_list), comment_lines, original_content: b_content, }) } fn unformat_comment(c: &str) -> String { fn remove_prepend(s: &str) -> String { let mut s = s.to_owned(); for to_remove in DOC_COMMENT_ID { s = s.replace(to_remove, ""); } /*for to_remove in COMMENT_ID { s = s.replace(to_remove, ""); }*/ if s.starts_with(" ") { (&s)[1..].to_owned() } else { s } } c.replace("*/", "") .split("\n") .into_iter() .map(|s| remove_prepend(s.trim_start())).collect::>().join("\n") } pub fn strip_comments(work_dir: &Path, path: &str, out_file: &mut F, ignore_macros: bool) { let full_path = work_dir.join(path); match build_event_list(&full_path) { Ok(parse_result) => { if parse_result.comment_lines.len() < 1 { return; } writeln!(out_file, "{}", &write_file(path)).unwrap(); let mut current: Option = None; let mut waiting_type: Option = None; let mut it = 0; while it < parse_result.event_list.len() { match parse_result.event_list[it].event { EventType::Type(ref t) => { if t.ty != Type::Unknown { waiting_type = Some(t.clone()); } } EventType::InScope => { current = add_to_type_scope(¤t, &waiting_type); waiting_type = None; } EventType::OutScope => { current = type_out_scope(¤t); waiting_type = None; } EventType::FileComment(ref c) => { // first, we need to find if it belongs to a mod if get_mod(¤t) == false { exit(1); } it += 1; let mut comments = format!("{}\n", &write_file_comment(&unformat_comment(c), ¤t, ignore_macros)); while match parse_result.event_list[it].event { EventType::FileComment(ref c) => { comments.push_str(&format!("{}\n", unformat_comment(c))); true } _ => false, } { it += 1; } write!(out_file, "{}", comments).unwrap(); continue; } EventType::Comment(ref c) => { let mut comments = format!("{}\n", c); it += 1; while it < parse_result.event_list.len() && match parse_result.event_list[it].event { EventType::Comment(ref c) => { comments.push_str(&format!("{}\n", c)); true } EventType::Type(_) => { false } _ => panic!("Doc comments cannot be written everywhere"), } { it += 1; } if it >= parse_result.event_list.len() { continue; } while match parse_result.event_list[it].event { EventType::Type(ref t) => { match t.ty { Type::Unknown => { match current { Some(ref cur) => { if cur.ty == Type::Enum || cur.ty == Type::Struct || cur.ty == Type::Use { if t.name == "pub" { true } else { let mut copy = t.clone(); copy.ty = Type::Variant; let tmp = add_to_type_scope(¤t, &Some(copy)); write!(out_file, "{}", write_comment(&tmp.unwrap(), &unformat_comment( &comments), ignore_macros)) .unwrap(); false } } else { t.name == "pub" } } None => t.name == "pub", } } _ => { let tmp = add_to_type_scope(¤t, &Some(t.clone())); write!(out_file, "{}", write_comment(&tmp.unwrap(), &unformat_comment(&comments), ignore_macros)).unwrap(); false } } } _ => panic!("An item was expected for this comment: {}", comments), } { it += 1; } continue; } } it += 1; } // we now remove doc comments from original file remove_comments(&full_path, &parse_result.comment_lines, parse_result.original_content); } Err(e) => { println!("Unable to open \"{}\": {}", path, e); } } } fn remove_comments(path: &Path, to_remove: &[usize], mut o_content: Vec) { match File::create(path) { Ok(mut f) => { let mut decal = 0; for line in to_remove { o_content.remove(line - decal); decal += 1; } write!(f, "{}", o_content.join("\n")).unwrap(); } Err(e) => { println!("Cannot open '{}': {}", path.display(), e); } } } rustdoc-stripper-0.1.9/src/types.rs010064400007650000024000000142171350423107500155770ustar0000000000000000// Copyright 2016 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::fmt::{Debug, Display, Error, Formatter}; use std::borrow::Borrow; #[derive(Debug, Clone)] pub struct ParseResult { pub event_list: Vec, pub comment_lines: Vec, pub original_content : Vec, } #[derive(Clone)] pub struct EventInfo { pub line: usize, pub event: EventType, } impl EventInfo { pub fn new(line: usize, event: EventType) -> EventInfo { EventInfo { line: line, event: event, } } } impl Debug for EventInfo { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { write!(fmt, "{:?}->{:?}", self.line, self.event) } } #[derive(Clone)] pub enum EventType { Comment(String), FileComment(String), Type(TypeStruct), InScope, OutScope, } impl Debug for EventType { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { match self { &EventType::Type(ref t) => write!(fmt, "Type: {:?}", t), &EventType::FileComment(ref t) => write!(fmt, "FileComment: {:?}", t), &EventType::Comment(ref t) => write!(fmt, "Comment: {:?}", t), &EventType::InScope => write!(fmt, "InScope"), &EventType::OutScope => write!(fmt, "OutScope"), } } } #[derive(Clone, PartialEq)] pub struct TypeStruct { pub ty: Type, pub parent: Option>, pub name: String, pub args: Vec, } impl TypeStruct { pub fn new(ty: Type, name: &str) -> TypeStruct { TypeStruct { ty: ty, name: name.to_owned(), args: vec!(), parent: None, } } /*pub fn from_args(ty: Type, args: Vec) -> TypeStruct { TypeStruct { ty: ty, name: String::new(), args: args, parent: None, } }*/ pub fn empty() -> TypeStruct { TypeStruct { ty: Type::Unknown, name: String::new(), args: Vec::new(), parent: None, } } pub fn get_depth(&self, ignore_macros: bool) -> usize { fn recur(ty: &Option>, is_parent: bool, ignore_macros: bool) -> usize { match ty { &Some(ref t) => { if ignore_macros && is_parent && t.ty == Type::Macro { recur(&t.parent, true, ignore_macros) } else { recur(&t.parent, true, ignore_macros) + 1 } }, _ => 0, } } recur(&self.parent, false, ignore_macros) } } impl Debug for TypeStruct { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { let parent = &self.parent; match parent { &Some(ref p) => write!(f, "{:?}::{} {}{}", p, self.ty, self.name, self.args.join(" ")), _ => write!(f, "{} {}{}", self.ty, self.name, self.args.join(" ")), } } } fn show(f: &mut Formatter, t: &TypeStruct, is_parent: bool) -> Result<(), Error> { if is_parent { write!(f, "{} {}{}::", t.ty, t.name, t.args.join(" ")) } else { write!(f, "{} {}{}", t.ty, t.name, t.args.join(" ")) } } fn sub_call(f: &mut Formatter, t: &TypeStruct, is_parent: bool) -> Result<(), Error> { if (t.ty == Type::Macro || t.ty.is_macro_definition()) && is_parent == true { match t.parent { Some(ref p) => sub_call(f, p.borrow(), true), _ => Ok(()), } } else { match t.parent { Some(ref p) => { try!(sub_call(f, p.borrow(), true)); show(f, t, is_parent) }, _ => show(f, t, is_parent), } } } impl Display for TypeStruct { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { sub_call(f, self, false) } } #[derive(Debug, Copy, Clone, PartialEq)] pub enum Type { Struct, Mod, Enum, Fn, Const, Static, Type, Variant, Impl, Use, MacroDefinition, Macro, Trait, Flags, Unknown, } impl Type { pub fn is_macro_definition(&self) -> bool { match *self { Type::MacroDefinition => true, _ => false, } } } impl Type { pub fn from(s: &str) -> Type { match s { "struct" => Type::Struct, "mod" => Type::Mod, "enum" => Type::Enum, "fn" => Type::Fn, "const" => Type::Const, "static" => Type::Static, "type" => Type::Type, "impl" => Type::Impl, "use" => Type::Use, "trait" => Type::Trait, "flags" => Type::Flags, "macro" => Type::Macro, "macro_rules" | "macro_rules!" => Type::MacroDefinition, _ => Type::Variant, } } } impl Display for Type { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match *self { Type::Struct => write!(f, "struct"), Type::Mod => write!(f, "mod"), Type::Enum => write!(f, "enum"), Type::Fn => write!(f, "fn"), Type::Const => write!(f, "const"), Type::Static => write!(f, "static"), Type::Type => write!(f, "type"), Type::Variant => write!(f, "variant"), Type::Impl => write!(f, "impl"), Type::Use => write!(f, "use"), Type::Trait => write!(f, "trait"), Type::Macro => write!(f, "macro"), Type::MacroDefinition => write!(f, "macro"), Type::Flags => write!(f, "flags"), _ => write!(f, "?"), } } } rustdoc-stripper-0.1.9/src/utils.rs010064400007650000024000000120721350176646200156020ustar0000000000000000// Copyright 2015 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::ffi::OsStr; use std::fs; use std::io; use std::io::prelude::*; use std::path::Path; use consts::{ MOD_COMMENT, FILE_COMMENT, FILE, END_INFO, OUTPUT_COMMENT_FILE, }; use types::TypeStruct; pub fn loop_over_files(path: &Path, func: &mut dyn FnMut(&Path, &str), files_to_ignore: &[S], verbose: bool) where S: AsRef { do_loop_over_files(path.as_ref(), path.as_ref(), func, files_to_ignore, verbose) } pub fn do_loop_over_files(work_dir: &Path, path: &Path, func: &mut dyn FnMut(&Path, &str), files_to_ignore: &[S], verbose: bool) where S: AsRef { match fs::read_dir(path) { Ok(it) => { let mut entries = vec!(); for entry in it { entries.push(entry.unwrap().path().to_owned()); } entries.sort(); for entry in entries { check_path_type(work_dir, &entry, func, files_to_ignore, verbose); } } Err(e) => { println!("Error while trying to iterate over {}: {}", path.display(), e); } } } fn check_path_type(work_dir: &Path, path: &Path, func: &mut dyn FnMut(&Path, &str), files_to_ignore: &[S], verbose: bool) where S: AsRef { match fs::metadata(path) { Ok(m) => { if m.is_dir() { if path == Path::new("..") || path == Path::new(".") { return; } do_loop_over_files(work_dir, path, func, files_to_ignore, verbose); } else { let path_suffix = strip_prefix(path, work_dir).unwrap(); let ignore = path == Path::new(&format!("./{}", OUTPUT_COMMENT_FILE)) || path.extension() != Some(OsStr::new("rs")) || files_to_ignore.iter().any(|s| s.as_ref() == path_suffix); if ignore { if verbose { println!("-> {}: ignored", path.display()); } return; } if verbose { println!("-> {}", path.display()); } func(work_dir, path_suffix.to_str().unwrap()); } } Err(e) => { println!("An error occurred on '{}': {}", path.display(), e); } } } pub fn join(s: &[String], join_part: &str) -> String { let mut ret = String::new(); let mut it = 0; while it < s.len() { ret.push_str(&s[it]); it += 1; if it < s.len() { ret.push_str(join_part); } } ret } // lifted from libstd for Path::strip_prefix is unstable fn strip_prefix<'a>(self_: &'a Path, base: &'a Path) -> Result<&'a Path, ()> { iter_after(self_.components(), base.components()) .map(|c| c.as_path()) .ok_or(()) } fn iter_after(mut iter: I, mut prefix: J) -> Option where I: Iterator + Clone, J: Iterator, A: PartialEq { loop { let mut iter_next = iter.clone(); match (iter_next.next(), prefix.next()) { (Some(x), Some(y)) => { if x != y { return None; } } (Some(_), None) => return Some(iter), (None, None) => return Some(iter), (None, Some(_)) => return None, } iter = iter_next; } } pub fn write_comment(id: &TypeStruct, comment: &str, ignore_macro: bool) -> String { if ignore_macro { format!("{}{}{}\n{}", MOD_COMMENT, id, END_INFO, comment) } else { format!("{}{:?}{}\n{}", MOD_COMMENT, id, END_INFO, comment) } } pub fn write_item_doc(w: &mut dyn Write, id: &TypeStruct, f: F) -> io::Result<()> where F: FnOnce(&mut dyn Write) -> io::Result<()> { try!(writeln!(w, "{}{}{}", MOD_COMMENT, id, END_INFO)); f(w) } pub fn write_file_comment(comment: &str, id: &Option, ignore_macro: bool) -> String { if let &Some(ref t) = id { if ignore_macro { format!("{} {}{}\n{}", FILE_COMMENT, t, END_INFO, comment) } else { format!("{} {:?}{}\n{}", FILE_COMMENT, t, END_INFO, comment) } } else { format!("{}{}\n{}", FILE_COMMENT, END_INFO, comment) } } pub fn write_file(file: &str) -> String { format!("{}{}{}", FILE, file, END_INFO) } pub fn write_file_name(w: &mut dyn Write, name: Option<&str>) -> io::Result<()> { writeln!(w, "{}{}{}", FILE, name.unwrap_or("*"), END_INFO) } rustdoc-stripper-0.1.9/tests/tests.rs010064400007650000024000001077471350423107500161630ustar0000000000000000// Copyright 2016 Gomez Guillaume // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. extern crate stripper_lib; extern crate tempfile; use std::fs::File; use std::io::prelude::*; use std::path::Path; use tempfile::{TempDir, tempdir}; const BASIC : &'static str = r#"//! File comment //! three //! lines /// struct Foo comment struct Foo { /// Foo comment /// fn some_func(a: u32, /// b: u32) {} A: u32, } mod Bar { //! mod comment test! { /// struct inside macro struct SuperFoo; sub_test! { /// and another one! struct FooFoo { x: u32, } } } mod SubBar { //! an empty mod //! yeay } } "#; const BASIC_STRIPPED: &'static str = r#"struct Foo { A: u32, } mod Bar { test! { struct SuperFoo; sub_test! { struct FooFoo { x: u32, } } } mod SubBar { } } "#; fn get_basic_md(file: &str) -> String { format!(r#" File comment three lines struct Foo comment Foo comment fn some_func(a: u32, b: u32) {{}} mod comment struct inside macro and another one! an empty mod yeay "#, file) } fn gen_file(temp_dir: &TempDir, filename: &str, content: &str) -> File { let mut f = File::create(temp_dir.path().join(filename)).expect("gen_file"); write!(f, "{}", content).unwrap(); f } fn compare_files(expected_content: &str, file: &Path) { let mut f = File::open(file).expect("compare_files '{}'"); let mut buf = String::new(); f.read_to_string(&mut buf).unwrap(); println!(""); for (l, r) in expected_content.lines().zip(buf.lines()) { assert_eq!(l, r, "compare_files0 failed"); println!("{}", l); } assert_eq!(expected_content, &buf, "compare_files1 failed"); } #[allow(unused_must_use)] #[test] fn test_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } compare_files(&get_basic_md(test_file), &temp_dir.path().join(comment_file)); compare_files(BASIC_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, false); compare_files(BASIC, &temp_dir.path().join(test_file)); } const BASIC2: &'static str = r#" use Bin; use Box; use Buildable; use Container; use Widget; use Window; use ffi; use glib::object::Downcast; use glib::object::IsA; use glib::translate::*; glib_wrapper! { /// Dialog boxes are a convenient way to prompt the user for a small amount /// of input, e.g. to display a message, ask a question, or anything else /// that does not require extensive effort on the user’s part. /// /// ``` /// { /// dialog = gtk_dialog_new_with_buttons ("Message", /// parent, /// flags, /// _("_OK"), /// GTK_RESPONSE_NONE, /// NULL); /// } /// ``` pub struct Dialog(Object): Widget, Container, Bin, Window, Buildable; match fn { get_type => || ffi::gtk_dialog_get_type(), } } impl Dialog { /// Creates a new dialog box. /// /// Widgets should not be packed into this `Window` /// directly, but into the `vbox` and `action_area`, as described above. /// /// # Returns /// /// the new dialog as a `Widget` pub fn new() -> Dialog { assert_initialized_main_thread!(); unsafe { Widget::from_glib_none(ffi::gtk_dialog_new()).downcast_unchecked() } } //pub fn new_with_buttons>(title: Option<&str>, parent: Option<&T>, flags: DialogFlags, first_button_text: Option<&str>, : /*Unknown conversion*//*Unimplemented*/Fundamental: VarArgs) -> Dialog { // unsafe { TODO: call ffi::gtk_dialog_new_with_buttons() } //} } /// Trait containing all `Dialog` methods. pub trait DialogExt { /// Adds an activatable widget to the action area of a `Dialog`, /// connecting a signal handler that will emit the `Dialog::response` /// signal on the dialog when the widget is activated. The widget is /// appended to the end of the dialog’s action area. If you want to add a /// non-activatable widget, simply pack it into the `action_area` field /// of the `Dialog` struct. fn add_action_widget>(&self, child: &T, response_id: i32); /// Adds a button with the given text fn add_button(&self, button_text: &str, response_id: i32) -> Widget; } "#; const BASIC2_STRIPPED : &'static str = r#" use Bin; use Box; use Buildable; use Container; use Widget; use Window; use ffi; use glib::object::Downcast; use glib::object::IsA; use glib::translate::*; glib_wrapper! { pub struct Dialog(Object): Widget, Container, Bin, Window, Buildable; match fn { get_type => || ffi::gtk_dialog_get_type(), } } impl Dialog { pub fn new() -> Dialog { assert_initialized_main_thread!(); unsafe { Widget::from_glib_none(ffi::gtk_dialog_new()).downcast_unchecked() } } //pub fn new_with_buttons>(title: Option<&str>, parent: Option<&T>, flags: DialogFlags, first_button_text: Option<&str>, : /*Unknown conversion*//*Unimplemented*/Fundamental: VarArgs) -> Dialog { // unsafe { TODO: call ffi::gtk_dialog_new_with_buttons() } //} } pub trait DialogExt { fn add_action_widget>(&self, child: &T, response_id: i32); fn add_button(&self, button_text: &str, response_id: i32) -> Widget; } "#; fn get_basic2_md(file: &str) -> String { format!(r#" Dialog boxes are a convenient way to prompt the user for a small amount of input, e.g. to display a message, ask a question, or anything else that does not require extensive effort on the user’s part. ``` {{ dialog = gtk_dialog_new_with_buttons ("Message", parent, flags, _("_OK"), GTK_RESPONSE_NONE, NULL); }} ``` Creates a new dialog box. Widgets should not be packed into this `Window` directly, but into the `vbox` and `action_area`, as described above. # Returns the new dialog as a `Widget` Trait containing all `Dialog` methods. Adds an activatable widget to the action area of a `Dialog`, connecting a signal handler that will emit the `Dialog::response` signal on the dialog when the widget is activated. The widget is appended to the end of the dialog’s action area. If you want to add a non-activatable widget, simply pack it into the `action_area` field of the `Dialog` struct. Adds a button with the given text "#, file) } const BASIC2_MD: &'static str = r#" Dialog boxes are a convenient way to prompt the user for a small amount of input, e.g. to display a message, ask a question, or anything else that does not require extensive effort on the user’s part. ``` { dialog = gtk_dialog_new_with_buttons ("Message", parent, flags, _("_OK"), GTK_RESPONSE_NONE, NULL); } ``` Creates a new dialog box. Widgets should not be packed into this `Window` directly, but into the `vbox` and `action_area`, as described above. # Returns the new dialog as a `Widget` Creates a new `Dialog` with title `title` (or `None` for the default title; see `Window::set_title`) and transient parent `parent` (or `None` for none; see `Window::set_transient_for`). Trait containing all `Dialog` methods. Adds an activatable widget to the action area of a `Dialog`, connecting a signal handler that will emit the `Dialog::response` signal on the dialog when the widget is activated. The widget is appended to the end of the dialog’s action area. If you want to add a non-activatable widget, simply pack it into the `action_area` field of the `Dialog` struct. Adds a button with the given text "#; #[allow(unused_must_use)] #[test] fn test2_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC2); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, true); } compare_files(&get_basic2_md(test_file), &temp_dir.path().join(comment_file)); compare_files(BASIC2_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test2_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC2_STRIPPED); gen_file(&temp_dir, comment_file, BASIC2_MD); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), true, false); compare_files(BASIC2, &temp_dir.path().join(test_file)); } const BASIC3 : &'static str = r#"///struct Foo comment struct Foo; "#; const BASIC3_STRIPPED : &'static str = r#"struct Foo; "#; const BASIC3_REGEN : &'static str = r#"/// struct Foo comment struct Foo; "#; fn get_basic3_md(file: &str) -> String { format!(r#" struct Foo comment "#, file) } #[allow(unused_must_use)] #[test] fn test3_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC3); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } compare_files(&get_basic3_md(test_file), &temp_dir.path().join(comment_file)); compare_files(BASIC3_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test3_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC3_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic3_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, false); compare_files(BASIC3_REGEN, &temp_dir.path().join(test_file)); } const BASIC4 : &'static str = r#"// Copyright 2013-2015, The Gtk-rs Project Developers. // See the COPYRIGHT file at the top-level directory of this distribution. // Licensed under the MIT license, see the LICENSE file or use glib::translate::*; use ffi; use glib::object::Downcast; use Widget; glib_wrapper! { pub struct Socket(Object): Widget, ::Container, ::Buildable; match fn { get_type => || ffi::gtk_socket_get_type(), } } impl Socket { pub fn new() -> Socket { assert_initialized_main_thread!(); unsafe { Widget::from_glib_none(ffi::gtk_socket_new()).downcast_unchecked() } } /*pub fn add_id(&self, window: Window) { unsafe { ffi::gtk_socket_add_id(self.to_glib_none().0, window) }; } pub fn get_id(&self) -> Window { unsafe { ffi::gtk_socket_get_id(self.to_glib_none().0) }; } pub fn get_plug_window(&self) -> GdkWindow { let tmp_pointer = unsafe { ffi::gtk_socket_get_plug_window(self.to_glib_none().0) }; // add end of code }*/ } "#; fn get_basic4_md() -> String { String::new() } #[allow(unused_must_use)] #[test] fn test4_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC4); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } compare_files(&get_basic4_md(), &temp_dir.path().join(comment_file)); compare_files(BASIC4, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test4_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC4); gen_file(&temp_dir, comment_file, &get_basic4_md()); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, false); compare_files(BASIC4, &temp_dir.path().join(test_file)); } const BASIC5 : &'static str = r#"/// Here is a flags! pub flags SomeFlags : u32 { /// a const const VISIBLE = 1, /// another const HIDDEN = 2, } "#; const BASIC5_STRIPPED : &'static str = r#"pub flags SomeFlags : u32 { const VISIBLE = 1, const HIDDEN = 2, } "#; fn get_basic5_md(file: &str) -> String { format!(r#" Here is a flags! a const another "#, file) } #[allow(unused_must_use)] #[test] fn test5_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC5); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } compare_files(&get_basic5_md(test_file), &temp_dir.path().join(comment_file)); compare_files(BASIC5_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test5_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC5_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic5_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, false); compare_files(BASIC5, &temp_dir.path().join(test_file)); } const BASIC6 : &'static str = r#"/// not stripped comment struct Foo; impl Foo { /// another existing comment fn new() -> Foo {} } struct Bar; "#; const BASIC6_REGEN : &'static str = r#"/// not stripped comment struct Foo; impl Foo { /// another existing comment fn new() -> Foo {} } /// struct Bar comment struct Bar; "#; fn get_basic6_md(file: &str) -> String { format!(r#" struct Foo comment fn new comment struct Bar comment "#, file) } // test if ignore_doc_commented option is working #[allow(unused_must_use)] #[test] fn test6_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC6); gen_file(&temp_dir, comment_file, &get_basic6_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, true); compare_files(BASIC6_REGEN, &temp_dir.path().join(test_file)); } const BASIC7 : &'static str = r#"impl Foo { /// existing comment pub unsafe fn new() -> Foo {} } "#; fn get_basic7_md(file: &str) -> String { format!(r#" bad comment "#, file) } // test if ignore_doc_commented option is working #[allow(unused_must_use)] #[test] fn test7_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC7); gen_file(&temp_dir, comment_file, &get_basic7_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, true); compare_files(BASIC7, &temp_dir.path().join(test_file)); } // The goal of this test is to check if inner macro_rules doc comments are ignored. const BASIC8: &'static str = r#"/// foooo macro_rules! some_macro { ($constructor_ffi: ident) => { /// Takes full ownership of the output stream, /// which is not allowed to borrow any lifetime shorter than `'static`. /// /// Because the underlying `cairo_surface_t` is reference-counted, /// a lifetime parameter in a Rust wrapper type would not be enough to track /// how long it can keep writing to the stream. pub fn for_stream(width: f64, height: f64, stream: W) -> u32 { 0 } } } "#; const BASIC8_STRIPPED : &'static str = r#"macro_rules! some_macro { ($constructor_ffi: ident) => { /// Takes full ownership of the output stream, /// which is not allowed to borrow any lifetime shorter than `'static`. /// /// Because the underlying `cairo_surface_t` is reference-counted, /// a lifetime parameter in a Rust wrapper type would not be enough to track /// how long it can keep writing to the stream. pub fn for_stream(width: f64, height: f64, stream: W) -> u32 { 0 } } } "#; fn get_basic8_md(_file: &str) -> String { "\n\nfoooo\n".to_owned() } #[allow(unused_must_use)] #[test] fn test8_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC8); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } compare_files(&get_basic8_md(test_file), &temp_dir.path().join(comment_file)); compare_files(BASIC8_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test8_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC8); gen_file(&temp_dir, comment_file, &get_basic8_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, true); compare_files(BASIC8, &temp_dir.path().join(test_file)); } const BASIC9: &'static str = r#"trait SettingsBackendExt: 'static { /// Signals that the writability of all keys below a given path. pub fn path_writable_changed() {} } "#; const BASIC9_STRIPPED: &'static str = r#"trait SettingsBackendExt: 'static { pub fn path_writable_changed() {} } "#; fn get_basic9_md(file: &str) -> String { format!(r#" Signals that the writability of all keys below a given path. "#, file) } #[allow(unused_must_use)] #[test] fn test9_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC9); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } println!("Testing markdown"); compare_files(&get_basic9_md(test_file), &temp_dir.path().join(comment_file)); println!("Testing stripped file"); compare_files(BASIC9_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test9_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC9_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic9_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, true); compare_files(BASIC9, &temp_dir.path().join(test_file)); } const BASIC10: &'static str = r#"// This file was generated by gir (https://github.com/gtk-rs/gir) impl Device { /// Determines information about the current keyboard grab. /// This is not public API and must not be used by applications. /// /// # Deprecated since 3.16 /// /// The symbol was never meant to be used outside /// of GTK+ /// ## `display` /// the display for which to get the grab information /// ## `device` /// device to get the grab information from /// ## `grab_window` /// location to store current grab window /// ## `owner_events` /// location to store boolean indicating whether /// the `owner_events` flag to `gdk_keyboard_grab` or /// `gdk_pointer_grab` was `true`. /// /// # Returns /// /// `true` if this application currently has the /// keyboard grabbed. #[cfg_attr(feature = "v3_16", deprecated)] pub fn grab_info_libgtk_only(display: &Display, device: &Device) -> Option<(Window, bool)> { skip_assert_initialized!(); unsafe { let mut grab_window = ptr::null_mut(); let mut owner_events = mem::uninitialized(); let ret = from_glib(gdk_sys::gdk_device_grab_info_libgtk_only( display.to_glib_none().0, device.to_glib_none().0, &mut grab_window, &mut owner_events, )); if ret { Some((from_glib_none(grab_window), from_glib(owner_events))) } else { None } } } /// The ::changed signal is emitted either when the `Device` /// has changed the number of either axes or keys. For example /// In X this will normally happen when the slave device routing /// events through the master device changes (for example, user /// switches from the USB mouse to a tablet), in that case the /// master device will change to reflect the new slave device /// axes and keys. pub fn connect_changed(&self, f: F) -> SignalHandlerId { unsafe extern "C" fn changed_trampoline( this: *mut gdk_sys::GdkDevice, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"changed\0".as_ptr() as *const _, Some(transmute(changed_trampoline:: as usize)), Box_::into_raw(f), ) } } } "#; const BASIC10_STRIPPED: &'static str = r#"// This file was generated by gir (https://github.com/gtk-rs/gir) impl Device { #[cfg_attr(feature = "v3_16", deprecated)] pub fn grab_info_libgtk_only(display: &Display, device: &Device) -> Option<(Window, bool)> { skip_assert_initialized!(); unsafe { let mut grab_window = ptr::null_mut(); let mut owner_events = mem::uninitialized(); let ret = from_glib(gdk_sys::gdk_device_grab_info_libgtk_only( display.to_glib_none().0, device.to_glib_none().0, &mut grab_window, &mut owner_events, )); if ret { Some((from_glib_none(grab_window), from_glib(owner_events))) } else { None } } } pub fn connect_changed(&self, f: F) -> SignalHandlerId { unsafe extern "C" fn changed_trampoline( this: *mut gdk_sys::GdkDevice, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"changed\0".as_ptr() as *const _, Some(transmute(changed_trampoline:: as usize)), Box_::into_raw(f), ) } } } "#; fn get_basic10_md(file: &str) -> String { let x = r###" Determines information about the current keyboard grab. This is not public API and must not be used by applications. # Deprecated since 3.16 The symbol was never meant to be used outside of GTK+ ## `display` the display for which to get the grab information ## `device` device to get the grab information from ## `grab_window` location to store current grab window ## `owner_events` location to store boolean indicating whether the `owner_events` flag to `gdk_keyboard_grab` or `gdk_pointer_grab` was `true`. # Returns `true` if this application currently has the keyboard grabbed. The ::changed signal is emitted either when the `Device` has changed the number of either axes or keys. For example In X this will normally happen when the slave device routing events through the master device changes (for example, user switches from the USB mouse to a tablet), in that case the master device will change to reflect the new slave device axes and keys. "###; let mut y = format!("", file); y.push_str(x); y } #[allow(unused_must_use)] #[test] fn test10_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC10); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } println!("Testing markdown"); compare_files(&get_basic10_md(test_file), &temp_dir.path().join(comment_file)); println!("Testing stripped file"); compare_files(BASIC10_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test10_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC10_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic10_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), true, false); compare_files(BASIC10, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test10_regeneration2() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC10_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic10_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, true); compare_files(BASIC10, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test10_regeneration3() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC10_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic10_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), false, false); compare_files(BASIC10, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test10_regeneration4() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC10_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic10_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), true, true); compare_files(BASIC10, &temp_dir.path().join(test_file)); } const BASIC11: &'static str = r#"// This file was generated by gir (https://github.com/gtk-rs/gir) impl Device { /// The ::changed signal is emitted either when the `Device` /// has changed the number of either axes or keys. For example /// In X this will normally happen when the slave device routing /// events through the master device changes (for example, user /// switches from the USB mouse to a tablet), in that case the /// master device will change to reflect the new slave device /// axes and keys. pub fn connect_changed(&self, f: F) -> SignalHandlerId { unsafe extern "C" fn changed_trampoline( this: *mut gdk_sys::GdkDevice, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"changed\0".as_ptr() as *const _, Some(transmute(changed_trampoline:: as usize)), Box_::into_raw(f), ) } } /// The ::tool-changed signal is emitted on pen/eraser /// ``GdkDevices`` whenever tools enter or leave proximity. /// /// Feature: `v3_22` /// /// ## `tool` /// The new current tool #[cfg(any(feature = "v3_22", feature = "dox"))] pub fn connect_tool_changed( &self, f: F, ) -> SignalHandlerId { unsafe extern "C" fn tool_changed_trampoline( this: *mut gdk_sys::GdkDevice, tool: *mut gdk_sys::GdkDeviceTool, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this), &from_glib_borrow(tool)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"tool-changed\0".as_ptr() as *const _, Some(transmute(tool_changed_trampoline:: as usize)), Box_::into_raw(f), ) } } } "#; const BASIC11_STRIPPED: &'static str = r#"// This file was generated by gir (https://github.com/gtk-rs/gir) impl Device { pub fn connect_changed(&self, f: F) -> SignalHandlerId { unsafe extern "C" fn changed_trampoline( this: *mut gdk_sys::GdkDevice, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"changed\0".as_ptr() as *const _, Some(transmute(changed_trampoline:: as usize)), Box_::into_raw(f), ) } } #[cfg(any(feature = "v3_22", feature = "dox"))] pub fn connect_tool_changed( &self, f: F, ) -> SignalHandlerId { unsafe extern "C" fn tool_changed_trampoline( this: *mut gdk_sys::GdkDevice, tool: *mut gdk_sys::GdkDeviceTool, f: glib_sys::gpointer, ) { let f: &F = &*(f as *const F); f(&from_glib_borrow(this), &from_glib_borrow(tool)) } unsafe { let f: Box_ = Box_::new(f); connect_raw( self.as_ptr() as *mut _, b"tool-changed\0".as_ptr() as *const _, Some(transmute(tool_changed_trampoline:: as usize)), Box_::into_raw(f), ) } } } "#; fn get_basic11_md(file: &str) -> String { let x = r###" The ::changed signal is emitted either when the `Device` has changed the number of either axes or keys. For example In X this will normally happen when the slave device routing events through the master device changes (for example, user switches from the USB mouse to a tablet), in that case the master device will change to reflect the new slave device axes and keys. The ::tool-changed signal is emitted on pen/eraser ``GdkDevices`` whenever tools enter or leave proximity. Feature: `v3_22` ## `tool` The new current tool "###; let mut y = format!("", file); y.push_str(x); y } #[allow(unused_must_use)] #[test] fn test11_strip() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC11); { let mut f = gen_file(&temp_dir, comment_file, ""); stripper_lib::strip_comments(temp_dir.path(), test_file, &mut f, false); } println!("Testing markdown"); compare_files(&get_basic11_md(test_file), &temp_dir.path().join(comment_file)); println!("Testing stripped file"); compare_files(BASIC11_STRIPPED, &temp_dir.path().join(test_file)); } #[allow(unused_must_use)] #[test] fn test11_regeneration() { let test_file = "basic.rs"; let comment_file = "basic.md"; let temp_dir = tempdir().unwrap(); gen_file(&temp_dir, test_file, BASIC11_STRIPPED); gen_file(&temp_dir, comment_file, &get_basic11_md(test_file)); stripper_lib::regenerate_doc_comments(temp_dir.path().to_str().unwrap(), false, &temp_dir.path().join(comment_file).to_str().unwrap(), true, false); compare_files(BASIC11, &temp_dir.path().join(test_file)); } rustdoc-stripper-0.1.9/.cargo_vcs_info.json0000644000000001120000000000000144250ustar00{ "git": { "sha1": "fe542c98803a39310d4a75544b89edfee06e11db" } }