AI生成ツールの比較

1. Copilot Chat: Microsoft(+openAI) コーディング補助
2. Claude: Anthropic 長文補助
3. Gemini: Google 汎用AI・検索統合
4. Cursor: AI搭載のIDE、コーディングに特化
5. Devin: AIソフトウェアエンジニア, 自律型のコード開発

GeminiはChatGTPに比べてレスポンスの文章量が少ない印象。ChatGTPの方が丁寧で参考になりそう。
Devinは色々触って試してみたい。

ChatGTPとGithub Copilotの違い

### Chat GTP
– ブラウザやアプリでチャット形式
– モデル: GPT-4(Pro版)または GPT-3.5(無料)
– 補完のタイミング: 会話でまとめて質問・回答
– 用途: 質問・学習・エラー解析・仕様理解など広範囲
– 自然言語で深く対話可能
– 会話履歴を踏まえて提案

### GitHub Copilot
– Visual Studio Code / JetBrains / Neovim などで直接使う
– Codex(GPT-3系列)または Copilot専用モデル
– コードを書く「途中」でリアルタイム補完
– 手を動かしてコードを書くときの補助
– コメントやコードの一部を見て予測
– 開いているファイルや周辺コードだけで判断

Copilotでコードを書きながら、ChatGPTで「このコードの意味は?」「エラーの原因は?」と調べるのが効率的とのこと。

オンプレサーバの構成

[オンプレサーバの構成イメージ]

オンプレサーバ
 ↓
スイッチ
 ↓
ルータ or ファイヤウォール
 ↓
ONU(回線終端装置)
 ↓
光回線

### スイッチ: 社内機器を相互に接続する
中小企業向け: Cisco(Catalyst 1000 シリーズ), YAMAHA(SWX2200 シリーズ), NETGEAR(GS110TP, GS752TP)
中大企業向け: Cisco(Catalyst 9300, 9500シリーズ), Juniper(EX3400, EX4300), HPE Aruba(Aruba 2930F)

LAN内の通信を中継・管理
MACアドレスをもとに、必要なポートにだけデータを転送
**VLAN(仮想LAN)**によるセグメント分けも可能(セキュリティ・パフォーマンス向上)
L3スイッチなら、**セグメント間ルーティング(簡易ルータ機能)**も持つ

### ルータ/ファイヤフォール
中小企業向け: Cisco(Catalyst 1000 シリーズ), YAMAHA(SWX2200 シリーズ), NETGEAR(GS110TP, GS752TP)
中大企業向け: Palo Alto Networks, Fortinet, Cisco

ルータ:異なるネットワーク間(例:社内LAN ↔ インターネット)を中継
ファイアウォール:通信のフィルタリング・遮断で、社内ネットワークを保護
ルータの主な機能:
パケットの転送(ルーティング)
NAT変換(社内のプライベートIP ⇄ インターネットのグローバルIP)
DHCPやDNSリレー機能など
🟥 ファイアウォールの主な機能:
ポート制御/IP制限/アクセス制御(ACL)
外部からの不正アクセスをブロック
次世代型ではアンチウイルスやアプリ制御も(UTM機能)

### ONU(光回線終端装置)
NTT東西: PR-500KI、RT-500MI、RV-440NEなど
KDDI(auひかり): BL1000HW など
ソフトバンク光: HGW(型番非公開が多い)
—-
役割:光ファイバー信号(アナログ)を、デジタル信号に変換してルータへ渡す中継機器

amazonで製品名を検索すると、法人向けスイッチやルータ/ファイヤウォールがどんなものかイメージが掴める。

main.rsとlib.rsの分割

lib.rs と main.rs の両方があると、Rust は自動的にlib.rs をライブラリとしてビルドする仕組みとなっている。

lib.rs

pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

main.rs

use app::greet;

fn main() {
    
    let name = "Alice";
    println!("{}", greet(name));
}

Compiling app v0.1.0 (/home/vagrant/dev/work/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.45s
Running `target/debug/app`
Hello, Alice!

なるほど、lib.rs はRust関連の書籍を見るとよく出てきますが、ようやく意味がわかりました。

Rustでデータベースを設計しよう9(テストケース)

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    fn create_test_table() -> Table {
        let mut schema = HashMap::new();
        let mut order = vec![];

        let columns = vec![
            ("id", ColumnType::Integer, Constraint::PrimaryKey),
            ("name", ColumnType::Text, Constraint::None),
            ("age", ColumnType::Integer, Constraint::None),
        ];

        for(i, (name, col_type, constraint)) in columns.into_iter().enumerate(){
            schema.insert(
                name.to_string(),
                ColumnSchema {
                    order: i,
                    column_type: col_type,
                    constraint,
                },
            );
            order.push(name.to_string());
        }

        Table {
            name: "users".to_string(),
            columns: Columns { schema, order },
            rows: vec![],
        }
    }

    #[test]
    fn test_insert_and_find() {
        let mut table = create_test_table();

        let result = table.insert_row(vec![
            Value::Integer(1),
            Value::Text("Alice".to_string()),
            Value::Integer(30),
        ]);
        assert!(result.is_ok());

        let results = table.find_rows_by_column_value("id", &Value::Integer(1));
        assert_eq!(results.len(), 1);
        assert_eq!(results[0][1], Value::Text("Alice".to_string()));
    }

    #[test]
    fn test_update_by_condition() {
        let mut table = create_test_table();
        table.insert_row(vec![
            Value::Integer(2),
            Value::Text("Bob".to_string()),
            Value::Integer(25),
        ]).unwrap();

        let condition = Condition::Equals("id".to_string(), Value::Integer(2));
        let updated = table.update_by_condition("name", Value::Text("Carol".to_string()), Some(condition)).unwrap();
        assert_eq!(updated, 1);

        let updated_rows = table.find_rows_by_column_value("id", &Value::Integer(2));
        assert_eq!(updated_rows[0][1], Value::Text("Carol".to_string()));
    }

    #[test]
    fn test_delete_by_condition() {
        let mut table = create_test_table();
        table.insert_row(vec![
            Value::Integer(3),
            Value::Text("David".to_string()),
            Value::Integer(40),
        ]).unwrap();

        let condition = Condition::Equals("id".to_string(), Value::Integer(3));
        let deleted = table.delete_by_condition(Some(condition)).unwrap();
        assert_eq!(deleted, 1);

        let rows = table.find_rows_by_column_value("id", &Value::Integer(3));
        assert_eq!(rows.len(), 0);
    }

    #[test]
    fn test_unique_violation() {
        let mut table = create_test_table();
        table.insert_row(vec![
            Value::Integer(4),
            Value::Text("Eve".to_string()),
            Value::Integer(50),
        ]).unwrap();

        let result = table.insert_row(vec![
            Value::Integer(4),
            Value::Text("Eve2".to_string()),
            Value::Integer(60),
        ]);

        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Duplicate primary key"));
    }

}

Finished `test` profile [unoptimized + debuginfo] target(s) in 4.35s
Running unittests src/main.rs (target/debug/deps/rds-a17132f561100387)

running 4 tests
test tests::test_insert_and_find … ok
test tests::test_delete_by_condition … ok
test tests::test_unique_violation … ok
test tests::test_update_by_condition … ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

プロジェクトを lib.rs + main.rs に分けたい。

Rustでデータベースを設計しよう8(create tableのパーサ)

use regex::Regex;

fn main() {
    
    let text = "CREATE TABLE users (
        id integer,
        name text,
        age integer,
    );";
    println!("{}", text);

    let table_re = Regex::new(r"(?i)create\s+table\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\((?s)(.*?)\)").unwrap();

    if let Some(caps) = table_re.captures(text) {
        let table_name = &caps[1];
        let columns_str = &caps[2];
        println!("table name: {}", table_name);

        let column_re = Regex::new(r"(?i)([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z0-9()]+)").unwrap();

        for line in columns_str.lines() {
            if let Some(col_caps) = column_re.captures(line.trim()) {
                let column_name = &col_caps[1];
                let column_type = &col_caps[2];
                println!(" column name: {}, type: {}", column_name, column_type);
            }
        }
    } else {
        println!("CREATE TABLE 文ではありません");
    }
}

CREATE TABLE users (
id integer,
name text,
age integer,
);
table name: users
column name: id, type: integer
column name: name, type: text
column name: age, type: integer

これにprimary keyなどのconstrainも追加したい…

fn main() {
    
    let text = "CREATE TABLE users (
        id integer primarykey,
        name text not null,
        age integer
    );";
    println!("{}", text);

    let table_re = Regex::new(r"(?is)create\s+table\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*?)\)").unwrap();

    if let Some(caps) = table_re.captures(text) {
        let table_name = &caps[1];
        let columns_str = &caps[2];
        println!("table name: {}", table_name);

        let column_re = Regex::new(r"(?i)^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s+([a-zA-Z0-9()]+)(?:\s+(.*))?[,]?$").unwrap();

        for line in columns_str.lines() {

            let line = line.trim();
            if line.is_empty() {
                continue;
            }


            if let Some(col_caps) = column_re.captures(line) {
                let column_name = &col_caps[1];
                let column_type = &col_caps[2];
                let constraint = col_caps.get(3).map_or("", |m| m.as_str()).trim();
                println!(" column name: {}, type: {}, constraint: {}", column_name, column_type, if constraint.is_empty() { "None" } else { constraint });
            } else {
                println!(" ignored row: {}", line);
            }
        }
    } else {
        println!("CREATE TABLE 文ではありません");
    }
}

CREATE TABLE users (
id integer primarykey,
name text not null,
age integer
);
table name: users
column name: id, type: integer, constraint: primarykey,
column name: name, type: text, constraint: not null,
column name: age, type: integer, constraint: None

Rustでデータベースを設計しよう7(binary書き込み/読み込み)

cargo.tomlでbincode = “1.3”を設定する

    fn save_to_binary_file(&self, path: &str) -> Result<(), String> {
        let encoded = bincode::serialize(self).map_err(|e| e.to_string())?;
        let mut file = File::create(path).map_err(|e| e.to_string())?;
        file.write_all(&encoded).map_err(|e| e.to_string())?;
        Ok(())
    }

    fn load_from_binary_file(path: &str) -> Result<Table, String> {
        let mut file = File::open(path).map_err(|e| e.to_string())?;
        let mut buffer = Vec::new();
        file.read_to_end(&mut buffer).map_err(|e| e.to_string())?;
        let table: Table = bincode::deserialize(&buffer).map_err(|e| e.to_string())?;
        Ok(table)
    }

fn main()

    let mut table = create_sample_table();

    table.save_to_binary_file("users.bin").expect("Failed to save binary");
    let loaded = Table::load_from_binary_file("users.bin").expect("Failed to load binary");
    println!("読み込んだtable: {:?}", loaded);

読み込んだtable: Table { name: “users”, columns: Columns { schema: {“name”: ColumnSchema { order: 1, column_type: Text, constraint: None }, “id”: ColumnSchema { order: 0, column_type: Integer, constraint: PrimaryKey }, “age”: ColumnSchema { order: 2, column_type: Integer, constraint: None }}, order: [“id”, “name”, “age”] }, rows: [[Integer(1), Text(“Alice”), Integer(30)], [Integer(2), Text(“Bob”), Integer(25)]] }

$ cat users.bin
usersidnameageidnameageAliceBob

Rustでデータベースを設計しよう6(json書き込み/読み込み)

impl Tableで、jsonの保存、読み取り関数を書く。export_to_json()でjson化を関数にしているので、それを実行して、write_allで書き込む。読み取りの場合は、read_to_string()

    fn save_to_file(&self, path: &str) -> Result<(), String> {
        let json = self.export_to_json();
        let serialized = serde_json::to_string_pretty(&json)
            .map_err(|e| e.to_string())?;

        let mut file = File::create(path).map_err(|e| e.to_string())?;
        file.write_all(serialized.as_bytes()).map_err(|e| e.to_string())?;

        Ok(())
    }

    fn load_from_file(path: &str) -> Result<Table, String> {
        let mut file = File::open(path).map_err(|e| e.to_string())?;
        let mut contents = String::new();
        file.read_to_string(&mut contents).map_err(|e| e.to_string())?;

        let json: JsonValue = serde_json::from_str(&contents).map_err(|e| e.to_string())?;

        Table::import_from_json(&json)
    }

### 保存、読み取りの実行
サンプルテーブルは関数で別に作っておく

fn create_sample_table() -> Table {
    let mut schema = HashMap::new();
    let mut order = vec![];

    let columns = vec![
        ("id", ColumnType::Integer, Constraint::PrimaryKey),
        ("name", ColumnType::Text, Constraint::None),
        ("age", ColumnType::Integer, Constraint::None),
    ];

    for (i, (name, col_type, constraint)) in columns.into_iter().enumerate() {
        schema.insert(name.to_string(), ColumnSchema {
            order: i,
            column_type: col_type,
            constraint,
        });
        order.push(name.to_string());
    }
    let columns = Columns { schema, order };

    let mut table = Table {
        name: "users".to_string(),
        columns: columns,
        rows: vec![],
    };

    let result = table.insert_row(vec![
        Value::Integer(1),
        Value::Text("Alice".to_string()),
        Value::Integer(30),
    ]);

    let result = table.insert_row(vec![
        Value::Integer(2),
        Value::Text("Bob".to_string()),
        Value::Integer(25),
    ]);

    table
}
    let mut table = create_sample_table();

    if let Err(e) = table.save_to_file("users.json") {
        eprintln!("保存に失敗しました: {}", e);
    }

    match Table::load_from_file("users.json") {
        Ok(loaded_table) => {
            println!("読み込み成功: {:?}", loaded_table);
        }
        Err(e) => {
            eprintln!("読み込み失敗: {}", e);
        }
    }

読み込み成功: Table { name: “users”, columns: Columns { schema: {“id”: ColumnSchema { order: 0, column_type: Integer, constraint: PrimaryKey }, “age”: ColumnSchema { order: 2, column_type: Integer, constraint: None }, “name”: ColumnSchema { order: 1, column_type: Text, constraint: None }}, order: [“id”, “name”, “age”] }, rows: [[Integer(1), Text(“Alice”), Integer(30)], [Integer(2), Text(“Bob”), Integer(25)]] }

### psqlのデータ保存
– 独自のフォーマットでファイルにテーブルデータを格納
– 各テーブルは内部的に「リレーション」として表され、base/ ディレクトリ以下にファイルとして存在
– PostgreSQL はデフォルトで 8KB 単位のページにデータを分割して保存

### mysqlのデータ保存
– 通常は /var/lib/mysql/ ディレクトリに保存されます。各データベースはサブディレクトリとして管理
– デフォルトでは1つの共有表領域ファイル(ibdata1)にすべてのデータを保存

どちらのDBMSも、保存しているファイルは バイナリ形式

Rustでデータベースを設計しよう5(複合条件)

#[derive(Debug, Clone)]
enum Condition {
    Equals(String, Value),
    NotEquals(String, Value),
    And(Box<Condition>, Box<Condition>),
    Or(Box<Condition>, Box<Condition>),
}
    fn row_matches_condition(&self, row: &Vec<Value>, condition: &Condition) -> bool {
        match condition {
            Condition::Equals(col, val) => {
                if let Some(schema) = self.columns.schema.get(col) {
                    row.get(schema.order) == Some(val)
                } else {
                    false
                }
            }
            Condition::NotEquals(col, val) => {
                if let Some(schema) = self.columns.schema.get(col) {
                    row.get(schema.order) != Some(val)
                } else {
                    false
                }
            }
            Condition::And(lhs, rhs) => {
                self.row_matches_condition(row, lhs) && self.row_matches_condition(row, rhs)
            }
            Condition::Or(lhs, rhs) => {
                self.row_matches_condition(row, lhs) || self.row_matches_condition(row, rhs)
            }
        }
    }

    fn find_rows_by_condition(&self, condition: &Condition) -> Vec<&Vec<Value>> {
        self.rows
            .iter()
            .filter(|row| self.row_matches_condition(row, condition))
            .collect()
    }

// 

    let _ = table.update_value_by_column_condition(
        "id",
        &Value::Integer(2),
        "name",
        Value::Text("Carol".to_string()),
    );

    let condition = Condition::And(
        Box::new(Condition::Equals("age".to_string(), Value::Integer(25))),
        Box::new(Condition::NotEquals("name".to_string(), Value::Text("Bob".to_string()))),
    );

    let results = table.find_rows_by_condition(&condition);
    for row in results {
        println!("Match row: {:?}", row);
    }