Java 인터페이스 ↔ 트레이트 문서의 검증 코드다.

//! 16 Java 인터페이스 ↔ 트레이트: Java 문서의 RemoteControl 예를 트레이트로 옮기고, 같은 점과 다른 점을 확인한다.
//! 남의 타입에 내 트레이트 붙이기, 필드 없음, 기본 메서드 이름 충돌(E0034), 변수 타입이 부를 수 있는 메서드를 정함(E0599),
//! 슈퍼트레이트와 업캐스팅(1.86), 정적 디스패치 기본값, 연관 상수.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/3.개념집/16_java-interface-vs-trait_verify.rs"

use std::sync::atomic::{AtomicUsize, Ordering};

static SNIPPET: AtomicUsize = AtomicUsize::new(0);

fn compile_errors(code: &str) -> String {
    let n = SNIPPET.fetch_add(1, Ordering::Relaxed);
    let dir = std::env::temp_dir().join(format!("vr16_{}_{}", std::process::id(), n));
    std::fs::create_dir_all(&dir).unwrap();
    let file = dir.join("snippet.rs");
    std::fs::write(&file, code).unwrap();
    let out = std::process::Command::new("rustc")
        .args([
            "--edition",
            "2024",
            "--crate-type=lib",
            "--emit=metadata",
            "--out-dir",
        ])
        .arg(&dir)
        .arg(&file)
        .output()
        .expect("rustc 실행");
    String::from_utf8_lossy(&out.stderr).into_owned()
}

fn expect_error(label: &str, code: &str, needle: &str) {
    let e = compile_errors(code);
    let first = e
        .lines()
        .find(|l| l.starts_with("error"))
        .unwrap_or("(오류 없음)");
    println!("[{label}] {first}");
    assert!(e.contains(needle), "{label}: {e}");
}

// Java의 interface RemoteControl { int MAX_VOLUME = 10; void turnOn(); void turnOff(); }
trait RemoteControl {
    fn turn_on(&self) -> String;
    fn turn_off(&self) -> String;
    // Java의 default 메서드에 해당한다.
    fn restart(&self) -> String {
        format!("{} / {}", self.turn_off(), self.turn_on())
    }
}

// Java 인터페이스의 상수 필드는 연관 상수로 옮긴다. 연관 상수가 있는 트레이트는 dyn으로 못 쓰므로 따로 둔다.
trait Volume {
    const MAX_VOLUME: u8 = 10;
}

struct Television;
struct Lights;

impl Volume for Television {}
impl Volume for Lights {
    const MAX_VOLUME: u8 = 0;
}

impl RemoteControl for Television {
    fn turn_on(&self) -> String {
        "Turn on TV".to_string()
    }
    fn turn_off(&self) -> String {
        "Turn off TV".to_string()
    }
}

impl RemoteControl for Lights {
    fn turn_on(&self) -> String {
        "Turn on lights".to_string()
    }
    fn turn_off(&self) -> String {
        "Turn off lights".to_string()
    }
}

// Java에서는 String에 내 인터페이스를 붙일 수 없다. Rust는 내 트레이트라면 남의 타입에도 붙인다.
trait Shouty {
    fn shouty(&self) -> String;
}

impl Shouty for String {
    fn shouty(&self) -> String {
        self.to_uppercase()
    }
}

// 정적 디스패치: 타입마다 함수가 따로 만들어진다.
fn press_static(rc: &impl RemoteControl) -> String {
    rc.turn_on()
}

// 동적 디스패치: Java의 인터페이스 변수처럼 실행 중에 고른다.
fn press_dyn(rc: &dyn RemoteControl) -> String {
    rc.turn_on()
}

// 인터페이스 상속에 해당하는 슈퍼트레이트
trait Searchable {
    fn search(&self, q: &str) -> String;
}

trait SmartDevice: RemoteControl + Searchable {}

struct SmartTelevision;

impl RemoteControl for SmartTelevision {
    fn turn_on(&self) -> String {
        "Turn on TV".to_string()
    }
    fn turn_off(&self) -> String {
        "Turn off TV".to_string()
    }
}

impl Searchable for SmartTelevision {
    fn search(&self, q: &str) -> String {
        format!("Search: {q}")
    }
}

impl SmartDevice for SmartTelevision {}

// 기본 메서드 이름이 겹칠 때
trait Walker {
    fn hello(&self) -> &'static str {
        "Walker hello"
    }
}

trait Talker {
    fn hello(&self) -> &'static str {
        "Talker hello"
    }
}

struct Robot;
impl Walker for Robot {}
impl Talker for Robot {}

fn main() {
    // 1. 같은 호출이 든 값에 따라 다른 일을 한다 (Java 문서 C4와 같은 배열)
    let rcs: Vec<Box<dyn RemoteControl>> =
        vec![Box::new(Lights), Box::new(Television), Box::new(Lights)];
    let out: Vec<String> = rcs.iter().map(|rc| rc.turn_on()).collect();
    println!("{out:?}");
    assert_eq!(out, ["Turn on lights", "Turn on TV", "Turn on lights"]);

    // 2. 기본 메서드
    assert_eq!(Television.restart(), "Turn off TV / Turn on TV");

    // 3. 연관 상수: 구현마다 값을 바꿀 수 있다 (Java 인터페이스 상수는 하나뿐).
    println!(
        "MAX_VOLUME: TV {}, Lights {}",
        Television::MAX_VOLUME,
        Lights::MAX_VOLUME
    );
    assert_eq!(
        (
            <Television as Volume>::MAX_VOLUME,
            <Lights as Volume>::MAX_VOLUME
        ),
        (10, 0)
    );

    expect_error(
        "연관 상수가 있는 트레이트를 dyn으로 사용",
        "pub trait RemoteControl { const MAX_VOLUME: u8 = 10; fn on(&self); }
         pub fn f(_x: &dyn RemoteControl) {}",
        "error[E0038]",
    );

    // 4. 남의 타입(String)에 내 트레이트
    assert_eq!(String::from("abc").shouty(), "ABC");

    // 5. 정적·동적 디스패치 모두 같은 결과. 포인터 크기는 다르다.
    assert_eq!(press_static(&Lights), press_dyn(&Lights));
    let w = std::mem::size_of::<usize>();
    assert_eq!(std::mem::size_of::<&Lights>(), w);
    assert_eq!(std::mem::size_of::<&dyn RemoteControl>(), 2 * w);
    println!(
        "&Lights {}바이트, &dyn RemoteControl {}바이트",
        std::mem::size_of::<&Lights>(),
        std::mem::size_of::<&dyn RemoteControl>()
    );

    // 6. 슈퍼트레이트와 업캐스팅
    let tv = SmartTelevision;
    let smart: &dyn SmartDevice = &tv;
    let rc: &dyn RemoteControl = smart; // 1.86부터 되는 업캐스팅
    let sc: &dyn Searchable = smart;
    assert_eq!(rc.turn_on(), "Turn on TV");
    assert_eq!(sc.search("rust"), "Search: rust");
    expect_error(
        "슈퍼트레이트 하나를 빠뜨림",
        "pub trait A { fn a(&self); } pub trait B { fn b(&self); }
         pub trait C: A + B {}
         pub struct X;
         impl A for X { fn a(&self) {} }
         impl C for X {}",
        "error[E0277]",
    );
    expect_error(
        "변수 타입에 없는 메서드 호출",
        "pub trait A { fn a(&self); } pub trait B { fn b(&self); }
         pub fn f(x: &dyn A) { x.b(); }",
        "error[E0599]",
    );

    // 7. 기본 메서드 이름 충돌: 부를 때 모호하다. 어느 트레이트인지 적으면 풀린다.
    expect_error(
        "두 트레이트의 같은 이름 메서드를 그냥 부름",
        "pub trait Walker { fn hello(&self) -> u8 { 1 } }
         pub trait Talker { fn hello(&self) -> u8 { 2 } }
         pub struct Robot; impl Walker for Robot {} impl Talker for Robot {}
         pub fn f() -> u8 { Robot.hello() }",
        "error[E0034]",
    );
    let r = Robot;
    println!("{} / {}", Walker::hello(&r), <Robot as Talker>::hello(&r));
    assert_eq!(Walker::hello(&r), "Walker hello");
    assert_eq!(<Robot as Talker>::hello(&r), "Talker hello");

    // 8. 트레이트에는 필드를 둘 수 없다.
    expect_error(
        "트레이트에 필드 선언",
        "pub trait Counter { count: u32; }",
        "error",
    );

    // 9. 트레이트로는 값을 만들 수 없다 (Java의 new RemoteControl()에 해당).
    expect_error(
        "트레이트를 값처럼 사용",
        "pub trait RemoteControl { fn on(&self); }
         pub fn f() { let _x = RemoteControl; }",
        "error",
    );

    println!("ALL CHECKS PASSED");
}