rust/rhg/src/commands/cat.rs
changeset 49640 37bc3edef76f
parent 49631 c7fb9b74e753
child 49913 c15b415d1bff
equal deleted inserted replaced
49639:5844cd8e81ca 49640:37bc3edef76f
     2 use clap::Arg;
     2 use clap::Arg;
     3 use format_bytes::format_bytes;
     3 use format_bytes::format_bytes;
     4 use hg::operations::cat;
     4 use hg::operations::cat;
     5 use hg::utils::hg_path::HgPathBuf;
     5 use hg::utils::hg_path::HgPathBuf;
     6 use micro_timer::timed;
     6 use micro_timer::timed;
       
     7 use std::ffi::OsString;
       
     8 use std::os::unix::prelude::OsStrExt;
     7 
     9 
     8 pub const HELP_TEXT: &str = "
    10 pub const HELP_TEXT: &str = "
     9 Output the current or given revision of files
    11 Output the current or given revision of files
    10 ";
    12 ";
    11 
    13 
    12 pub fn args() -> clap::App<'static, 'static> {
    14 pub fn args() -> clap::Command {
    13     clap::SubCommand::with_name("cat")
    15     clap::command!("cat")
    14         .arg(
    16         .arg(
    15             Arg::with_name("rev")
    17             Arg::new("rev")
    16                 .help("search the repository as it is in REV")
    18                 .help("search the repository as it is in REV")
    17                 .short("-r")
    19                 .short('r')
    18                 .long("--rev")
    20                 .long("rev")
    19                 .value_name("REV")
    21                 .value_name("REV"),
    20                 .takes_value(true),
       
    21         )
    22         )
    22         .arg(
    23         .arg(
    23             clap::Arg::with_name("files")
    24             clap::Arg::new("files")
    24                 .required(true)
    25                 .required(true)
    25                 .multiple(true)
    26                 .num_args(1..)
    26                 .empty_values(false)
       
    27                 .value_name("FILE")
    27                 .value_name("FILE")
       
    28                 .value_parser(clap::value_parser!(std::ffi::OsString))
    28                 .help("Files to output"),
    29                 .help("Files to output"),
    29         )
    30         )
    30         .about(HELP_TEXT)
    31         .about(HELP_TEXT)
    31 }
    32 }
    32 
    33 
    39             "cat is disabled in rhg (enable it with 'rhg.cat = true' \
    40             "cat is disabled in rhg (enable it with 'rhg.cat = true' \
    40             or enable fallback with 'rhg.on-unsupported = fallback')",
    41             or enable fallback with 'rhg.on-unsupported = fallback')",
    41         ));
    42         ));
    42     }
    43     }
    43 
    44 
    44     let rev = invocation.subcommand_args.value_of("rev");
    45     let rev = invocation.subcommand_args.get_one::<String>("rev");
    45     let file_args = match invocation.subcommand_args.values_of("files") {
    46     let file_args =
    46         Some(files) => files.collect(),
    47         match invocation.subcommand_args.get_many::<OsString>("files") {
    47         None => vec![],
    48             Some(files) => files
    48     };
    49                 .filter(|s| !s.is_empty())
       
    50                 .map(|s| s.as_os_str())
       
    51                 .collect(),
       
    52             None => vec![],
       
    53         };
    49 
    54 
    50     let repo = invocation.repo?;
    55     let repo = invocation.repo?;
    51     let cwd = hg::utils::current_dir()?;
    56     let cwd = hg::utils::current_dir()?;
    52     let working_directory = repo.working_directory_path();
    57     let working_directory = repo.working_directory_path();
    53     let working_directory = cwd.join(working_directory); // Make it absolute
    58     let working_directory = cwd.join(working_directory); // Make it absolute
    54 
    59 
    55     let mut files = vec![];
    60     let mut files = vec![];
    56     for file in file_args.iter() {
    61     for file in file_args {
    57         if file.starts_with("set:") {
    62         if file.as_bytes().starts_with(b"set:") {
    58             let message = "fileset";
    63             let message = "fileset";
    59             return Err(CommandError::unsupported(message));
    64             return Err(CommandError::unsupported(message));
    60         }
    65         }
    61 
    66 
    62         let normalized = cwd.join(&file);
    67         let normalized = cwd.join(&file);
    63         // TODO: actually normalize `..` path segments etc?
    68         // TODO: actually normalize `..` path segments etc?
    64         let dotted = normalized.components().any(|c| c.as_os_str() == "..");
    69         let dotted = normalized.components().any(|c| c.as_os_str() == "..");
    65         if file == &"." || dotted {
    70         if file.as_bytes() == b"." || dotted {
    66             let message = "`..` or `.` path segment";
    71             let message = "`..` or `.` path segment";
    67             return Err(CommandError::unsupported(message));
    72             return Err(CommandError::unsupported(message));
    68         }
    73         }
    69         let relative_path = working_directory
    74         let relative_path = working_directory
    70             .strip_prefix(&cwd)
    75             .strip_prefix(&cwd)
    72         let stripped = normalized
    77         let stripped = normalized
    73             .strip_prefix(&working_directory)
    78             .strip_prefix(&working_directory)
    74             .map_err(|_| {
    79             .map_err(|_| {
    75                 CommandError::abort(format!(
    80                 CommandError::abort(format!(
    76                     "abort: {} not under root '{}'\n(consider using '--cwd {}')",
    81                     "abort: {} not under root '{}'\n(consider using '--cwd {}')",
    77                     file,
    82                     String::from_utf8_lossy(file.as_bytes()),
    78                     working_directory.display(),
    83                     working_directory.display(),
    79                     relative_path.display(),
    84                     relative_path.display(),
    80                 ))
    85                 ))
    81             })?;
    86             })?;
    82         let hg_file = HgPathBuf::try_from(stripped.to_path_buf())
    87         let hg_file = HgPathBuf::try_from(stripped.to_path_buf())