개그 지령'dont'를 만들었어요.

12765 단어 RustCLItech
명령하다했으니까 소개할게요.Rust가 있으면 다음과 같이 설치할 수 있습니다.
cargo install dont
(현재 구축된 바이너리나 각종 발표 소프트웨어 패키지가 존재하지 않습니다. 공헌을 환영합니다!)

이거 뭐야?


이름과 같이 지정한 처리를 실행하지 않는 명령입니다.예컨대
dont mkdir foo
foo를 만들지 않습니다.
dont 명령이 반드시'아무것도 하지 않는다'는 것은 아니다.특정한 모델에 대해 사용자의 의도를 고려하여 특별히 처리하다.readme에 몇 가지 예가 쓰여 있다.모든 모드를 알고 싶은 사람은 소스 코드를 참조하세요.

왜 이 타이밍에 공개됐냐면요.


만우절 대목으로 삼고 싶었는데 다른 농담거리와도 충돌이 있을 것 같아 일찍 공개하기로 했다.

설치 방법


기본적으로'출락'으로 마무리되지만, 실장에는 몇 가지 노하우가 있으니 다음은 소개한다.

-- 처리


이 명령은 명령행 분석기clap를 사용하여 부품 파라미터를 선택할 수 있습니다.현재 --help--version 등clap의 기본 명령만 실현되었다.
이 경우 - 로 시작하는 문자열을 하위 명령의 일부분으로 지정하려면 곤란합니다.이런 상황에서 대책으로 -- 영패는'나머지는 모두 위치 파라미터'라는 습관을 사용했다.
# 以下の2つは同じ意味
dont ls
dont -- ls
# 以下の2つは別の意味になる
dont --help  # ヘルプを表示
dont -- --help # "--help" はdontに対する指定の一部として解釈される
clap은 기본적으로 이런--에 대해 설명하지만 그 다음에 등장하는--도 다 먹는 문제가 있다.
dont -- ls  # 引数は ["ls"]
dont rm -- -r # 引数は ["rm", "--", "-r"] になってほしい
# ↑デフォルトでは ["rm", "-r"] になってしまう
dont에서 보듯이 위치 파라미터를 명령으로 재해석하려면 -- 이렇게 식용되면 사용자의 의도에 어긋날 수 있다.따라서 지정#[clap(allow_hyphen_values = true)]을 통해 최초의 위치 매개 변수 이후의 모든 매개 변수는 위치 매개 변수로 해석된다.

exec

dont 일정한 조건하에서 명령을 호출한다.이때 제어를 dont로 복구할 필요가 없기 때문에 유닉스계 환경execve(2)에서 자체로 교체하는 것이 좋다.일반적으로 이것은 다음과 같은 이유가 있다.
  • 불필요한 프로세스를 유지할 필요가 없습니다.
  • 유닉스 시스템의 환경에서pid=1은 특별히 취급된다.pid=1로 시작할 때, 때때로 pid가 하위 명령을 받는 것이 비교적 편리하다.
  • 특히 컨테이너의 보급으로 이른바 init 프로그램(systv init, upstart, systemd 등)을 제외한 pid=1 명령이 자주 사용되기 때문에 언뜻 시스템 프로그램이 아닌 프로그램이라도 이런 성질이 중요해진다.
  • dont 명령을 진지하게 사용하는 사람은 없지만 얻기 어려운 기회이기 때문에 예의를 지키기 위해dontfork-exec를 사용하지 않고 exec로 명령을 시작합니다.그러나 모든 OS에서 exec를 사용할 수 있는 것은 아니기 때문에 다음은 OS로 상황을 구분한다.
    cfg_if! {
        if #[cfg(unix)] {
            // unix系環境の場合はexecで起動し、dontコマンドに制御を戻さない
            use std::os::unix::process::CommandExt;
            result = Err(command.exec());
        } else {
            // unix系以外の環境の場合はサブプロセスとして起動する
            result = command.spawn();
        }
    }
    

    테스트

    dont 농담 지령이라고 하지만 그에 상응하는 복잡한 처리도 포함한다.앞으로 기능을 추가할 여지가 있기 때문에 시험을 써보려고 합니다.dont 중 아래 부분은 일정한 복잡성이 있기 때문에 테스트 대상에 포함된다.
  • 명령행 매개 변수의 해석
  • 사용자가 지정한 명령의 내용이나 주변 환경을 바탕으로 다음 처리 부분을 결정한다
  • 다른 한편, 다음 부분은 테스트에 포함되지 않습니다.
  • 실제 하위 명령 실행 섹션
  • 이유: exec 프로그램이 끝났기 때문에 단원 테스트에서 실행하기 어렵다.또 환경에 따라 실제 사용하는 지령도 다르지만 이를 배려하고 싶지 않다.이 부분은 자주 변경되는 곳이 아니기 때문에 수지가 맞지 않는다.
  • 테스트 대상을 이렇게 분할하기 위해 다음과 같은 enum을 정의했습니다.
    // dontコマンドの中核処理はこの値を返す
    #[derive(Debug, Clone, PartialEq, Eq)]
    enum Conclusion {
        Exit(i32),
        Exec(Vec<OsString>),
    }
    
    이것은 마지막으로 실행해야 할 처리를 나타낸다.실제 구축은 다음과 같은 처리를 지원한다.
  • Conclusion::Exitstd::process::exit
  • Conclusion::Execstd::os::unix::process::CommandExt::exec
  • 둘 다 성공할 때 복구되지 않기 때문에 한 번만 실행합니다.따라서 반환 값을 지정했습니다.
    한편, 반복되는 외부 처리를 테스트할 수 있도록 이동 가능한 다음trait를 정의했다.
    // 外界に問い合わせるときはこの値を使う。
    // 現在はコマンドの存在判定処理だけが定義されている。
    trait Controller {
        fn has_command(&self, name: &str) -> bool;
    }
    
    실제 구축에서 처리를 which crate에 인계하고dont 명령에서 특수 처리를 하지 않았다.그래서 이 부분은 테스트할 수 없습니다.
    dont 명령의 핵심 처리 execute 함수로 이 실례를 받아들인다.이번에는 정적 플로피 디스크를 사용했기 때문에 비용도 들지 않았다.
    // ctl が外界へのインターフェースになっている
    fn execute<C: Controller>(ctl: &C, args: &Args) -> Conclusion { /* ... */ }
    
    그러면 이렇게 외부 환경에 의존하는 처리 인터페이스화를 테스트할 때 다른 실시 방식으로 바꿀 수 있고 교체 방법으로 대체적으로 다음과 같은 두 가지 방법이 있다.
  • 실제 환경을 모방하는 행위를 대체하고 동적 응답의 실현을 되돌려준다.
  • 는 모든 테스트 항목에 필요한 모델에만 응답하는 실현(mock)으로 바꿉니다.
  • mock은 다음과 같은 상황에서 약점이 있다.
  • 입력은 각 테스트 용례의 흥미와 무관한 원인으로 인해 변화가 발생하기 쉬운 상황
  • 예를 들어 mock이 HTTP 요청에 대한 응답을 할 때 요청은 불확실성과 의존성을 많이 실현한다.(제목 순서, 타임 스탬프, JSON 키 순서 등)
  • matcher로 이러한 파동을 흡수하지 못하면 테스트 취미와 무관한 변경이라도 수정 테스트가 필요할 수 있다.
  • 출력에는 각 테스트 용례의 관심과 무관한 정보가 많이 포함된 경우
  • 사용자를 일람하는 요청에 응답할 때 열거된 사용자 ID는 테스트 용례의 고유한 취미로 제공하지만 사용자 데이터가 다른 곳에서 합성되는 경우 일반적으로 mock을 쓸 때 원래의 취미와 다른 정보는 테스트 용례에 남아 있기 쉽다.
  • 이번에 교체하고 싶은has_command 입력과 출력은 모두 간단하고 필요한 최소한이라고만 불리며 불릴 때 기대하는 행위는 테스트 사례에 따라 대부분 다르다.나는 마크가 비교적 적합하다고 생각해서 마크를 제공하기로 결정했다.
    Rust에서 mock을 제공하는 라이브러리가 여러 개였는데mockall 저자의 꾸준함을 느낄 수 있고 평가도 좋아서 이번에 사용하기로 했다.
    mockall에서attribute macro#[automock]를traait에 추가하면mock을 생성할 수 있습니다.실제 구축에서 모듈화할 필요가 없기 때문에 mockall는dev-dependenciescfg_attr에 미리 넣고 테스트할 때만 모듈을 생성하여 실현합니다.
    #[cfg_attr(test, mockall::automock)] // mock実装を生成する
    trait Controller {
        fn has_command(&self, name: &str) -> bool;
    }
    
    trait명에 Mock의 이름을 더한 struct(이 경우 MockController) 생성 모듈 설치.응답이 없는 모듈은 다음과 같이 생성할 수 있습니다.
    // 何も応答しない (常にpanicする)
    let ctl = MockController::new();
    
    이것을 교부하면 불려오지 않았음을 확인할 수 있습니다has_command.has_command의 호출을 테스트하려면 expect_*을 미리 불러주세요.
    // has_command("sl") に対してtrueを返すよう設定
    let mut ctl = MockController::new();
    ctl.expect_has_command().with(mockall::predicate::eq("sl")).returning(|_| true);
    

    최후

    dont 여러분의 공헌을 환영하라고 명령합니다.Rust에 뭔가를 쓰고 싶은 사람은 반드시 재미있는 도안을 추가한 홍보를 해 보세요.
    또한 qnighy/dont를 좋아한다면 스타를 더해 격려할 수 있다.

    좋은 웹페이지 즐겨찾기