this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

river

+280 -1
+222 -1
stack/river/cmd/river_cmd.ml
··· 606 606 1 607 607 ) $ output_dir_arg $ title_arg $ posts_per_page_arg) 608 608 609 + (* Category management commands *) 610 + module Category = struct 611 + let list state = 612 + let categories = River.State.list_categories state in 613 + if categories = [] then begin 614 + Fmt.pr "@.%a@.@." 615 + Fmt.(styled `Yellow string) 616 + "No categories defined yet. Use 'river category add' to create one." 617 + end else begin 618 + Fmt.pr "@.%a@." 619 + Fmt.(styled `Bold (styled (`Fg `Cyan) string)) 620 + (Printf.sprintf "Categories (%d total)" (List.length categories)); 621 + Fmt.pr "%a@.@." Fmt.(styled `Faint string) (String.make 60 '-'); 622 + List.iter (fun cat -> 623 + Fmt.pr "%a %a@." 624 + Fmt.(styled `Bold (styled (`Fg `Blue) string)) (River.Category.id cat) 625 + Fmt.(styled `Green string) (River.Category.name cat); 626 + (match River.Category.description cat with 627 + | Some desc -> 628 + Fmt.pr " %a %a@." 629 + Fmt.(styled `Faint string) "└─" 630 + Fmt.(styled `Faint string) desc 631 + | None -> ()); 632 + Fmt.pr "@." 633 + ) categories 634 + end; 635 + 0 636 + 637 + let add state ~id ~name ?description () = 638 + let category = River.Category.create ~id ~name ?description () in 639 + match River.State.add_category state category with 640 + | Ok () -> 641 + Fmt.pr "@.%a Category %a added successfully@.@." 642 + Fmt.(styled (`Fg `Green) string) "✓" 643 + Fmt.(styled `Bold string) id; 644 + 0 645 + | Error err -> 646 + Fmt.pr "@.%a Failed to add category: %s@.@." 647 + Fmt.(styled (`Fg `Red) string) "✗" 648 + err; 649 + 1 650 + 651 + let remove state ~id = 652 + match River.State.remove_category state ~id with 653 + | Ok () -> 654 + Fmt.pr "@.%a Category %a removed successfully@.@." 655 + Fmt.(styled (`Fg `Green) string) "✓" 656 + Fmt.(styled `Bold string) id; 657 + 0 658 + | Error err -> 659 + Fmt.pr "@.%a Failed to remove category: %s@.@." 660 + Fmt.(styled (`Fg `Red) string) "✗" 661 + err; 662 + 1 663 + 664 + let show state ~id = 665 + match River.State.get_category state ~id with 666 + | None -> 667 + Fmt.pr "@.%a Category %a not found@.@." 668 + Fmt.(styled (`Fg `Red) string) "✗ Error:" 669 + Fmt.(styled `Bold string) id; 670 + 1 671 + | Some cat -> 672 + Fmt.pr "@.%a@." 673 + Fmt.(styled `Bold (styled (`Fg `Cyan) string)) 674 + (Printf.sprintf "Category: %s" (River.Category.id cat)); 675 + Fmt.pr "%a@.@." Fmt.(styled `Faint string) (String.make 60 '-'); 676 + Fmt.pr "%a %a@." 677 + Fmt.(styled `Faint string) "Name: " 678 + Fmt.(styled `Green string) (River.Category.name cat); 679 + Fmt.pr "%a %a@.@." 680 + Fmt.(styled `Faint string) "Description:" 681 + Fmt.string (Option.value (River.Category.description cat) ~default:"(none)"); 682 + 683 + let post_ids = River.State.get_posts_by_category state ~category_id:id in 684 + Fmt.pr "%a@." 685 + Fmt.(styled `Bold string) 686 + (Printf.sprintf "Posts (%d)" (List.length post_ids)); 687 + Fmt.pr "%a@." Fmt.(styled `Faint string) (String.make 60 '-'); 688 + if post_ids = [] then 689 + Fmt.pr "%a@.@." 690 + Fmt.(styled `Faint string) 691 + " No posts tagged with this category." 692 + else begin 693 + List.iter (fun post_id -> 694 + Fmt.pr " %a %a@." 695 + Fmt.(styled `Faint string) "•" 696 + Fmt.(styled (`Fg `Blue) string) post_id 697 + ) post_ids; 698 + Fmt.pr "@." 699 + end; 700 + 0 701 + end 702 + 703 + (* Post category tagging commands *) 704 + module Post_category = struct 705 + let add state ~post_id ~category_id = 706 + match River.State.add_post_category state ~post_id ~category_id with 707 + | Ok () -> 708 + Fmt.pr "@.%a Post %a tagged with category %a@.@." 709 + Fmt.(styled (`Fg `Green) string) "✓" 710 + Fmt.(styled `Bold string) post_id 711 + Fmt.(styled `Bold string) category_id; 712 + 0 713 + | Error err -> 714 + Fmt.pr "@.%a Failed to tag post: %s@.@." 715 + Fmt.(styled (`Fg `Red) string) "✗" 716 + err; 717 + 1 718 + 719 + let remove state ~post_id ~category_id = 720 + match River.State.remove_post_category state ~post_id ~category_id with 721 + | Ok () -> 722 + Fmt.pr "@.%a Category %a removed from post %a@.@." 723 + Fmt.(styled (`Fg `Green) string) "✓" 724 + Fmt.(styled `Bold string) category_id 725 + Fmt.(styled `Bold string) post_id; 726 + 0 727 + | Error err -> 728 + Fmt.pr "@.%a Failed to remove category from post: %s@.@." 729 + Fmt.(styled (`Fg `Red) string) "✗" 730 + err; 731 + 1 732 + 733 + let list state ~post_id = 734 + let category_ids = River.State.get_post_categories state ~post_id in 735 + if category_ids = [] then begin 736 + Fmt.pr "@.%a@.@." 737 + Fmt.(styled `Yellow string) 738 + (Printf.sprintf "Post %s has no categories assigned." post_id) 739 + end else begin 740 + Fmt.pr "@.%a@." 741 + Fmt.(styled `Bold (styled (`Fg `Cyan) string)) 742 + (Printf.sprintf "Categories for post: %s" post_id); 743 + Fmt.pr "%a@.@." Fmt.(styled `Faint string) (String.make 60 '-'); 744 + List.iter (fun cat_id -> 745 + match River.State.get_category state ~id:cat_id with 746 + | Some cat -> 747 + Fmt.pr "%a %a@.@." 748 + Fmt.(styled `Bold (styled (`Fg `Blue) string)) cat_id 749 + Fmt.(styled `Green string) (River.Category.name cat) 750 + | None -> 751 + Fmt.pr "%a %a@.@." 752 + Fmt.(styled `Bold (styled (`Fg `Blue) string)) cat_id 753 + Fmt.(styled `Faint string) "(category not found)" 754 + ) category_ids 755 + end; 756 + 0 757 + end 758 + 759 + (* Cmdliner terms for category commands *) 760 + let category_list = 761 + Term.(const (fun env _xdg _profile -> 762 + let state = River.State.create env ~app_name:"river" in 763 + Category.list state 764 + )) 765 + 766 + let category_add = 767 + let id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"ID" ~doc:"Category ID (e.g., 'ocaml-projects')") in 768 + let name_arg = Arg.(required & pos 1 (some string) None & info [] ~docv:"NAME" ~doc:"Category display name") in 769 + let description_arg = Arg.(value & opt (some string) None & info ["d"; "description"] ~docv:"DESC" ~doc:"Category description") in 770 + Term.(const (fun id name description env _xdg _profile -> 771 + let state = River.State.create env ~app_name:"river" in 772 + Category.add state ~id ~name ?description () 773 + ) $ id_arg $ name_arg $ description_arg) 774 + 775 + let category_remove = 776 + let id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"ID" ~doc:"Category ID to remove") in 777 + Term.(const (fun id env _xdg _profile -> 778 + let state = River.State.create env ~app_name:"river" in 779 + Category.remove state ~id 780 + ) $ id_arg) 781 + 782 + let category_show = 783 + let id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"ID" ~doc:"Category ID to show") in 784 + Term.(const (fun id env _xdg _profile -> 785 + let state = River.State.create env ~app_name:"river" in 786 + Category.show state ~id 787 + ) $ id_arg) 788 + 789 + let category_cmd = 790 + let doc = "Manage custom categories" in 791 + let info = Cmd.info "category" ~doc in 792 + let list_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "list" ~doc:"List all categories") ~app_name:"river" ~service:"river" category_list in 793 + let add_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "add" ~doc:"Add a new category") ~app_name:"river" ~service:"river" category_add in 794 + let remove_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "remove" ~doc:"Remove a category") ~app_name:"river" ~service:"river" category_remove in 795 + let show_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "show" ~doc:"Show category details") ~app_name:"river" ~service:"river" category_show in 796 + Cmd.group info [list_cmd; add_cmd; remove_cmd; show_cmd] 797 + 798 + (* Cmdliner terms for post category tagging commands *) 799 + let tag_add = 800 + let post_id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"POST_ID" ~doc:"Post ID to tag") in 801 + let category_id_arg = Arg.(required & pos 1 (some string) None & info [] ~docv:"CATEGORY_ID" ~doc:"Category ID") in 802 + Term.(const (fun post_id category_id env _xdg _profile -> 803 + let state = River.State.create env ~app_name:"river" in 804 + Post_category.add state ~post_id ~category_id 805 + ) $ post_id_arg $ category_id_arg) 806 + 807 + let tag_remove = 808 + let post_id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"POST_ID" ~doc:"Post ID") in 809 + let category_id_arg = Arg.(required & pos 1 (some string) None & info [] ~docv:"CATEGORY_ID" ~doc:"Category ID to remove") in 810 + Term.(const (fun post_id category_id env _xdg _profile -> 811 + let state = River.State.create env ~app_name:"river" in 812 + Post_category.remove state ~post_id ~category_id 813 + ) $ post_id_arg $ category_id_arg) 814 + 815 + let tag_list = 816 + let post_id_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"POST_ID" ~doc:"Post ID") in 817 + Term.(const (fun post_id env _xdg _profile -> 818 + let state = River.State.create env ~app_name:"river" in 819 + Post_category.list state ~post_id 820 + ) $ post_id_arg) 821 + 822 + let tag_cmd = 823 + let doc = "Manage post category tags" in 824 + let info = Cmd.info "tag" ~doc in 825 + let add_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "add" ~doc:"Tag a post with a category") ~app_name:"river" ~service:"river" tag_add in 826 + let remove_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "remove" ~doc:"Remove a category from a post") ~app_name:"river" ~service:"river" tag_remove in 827 + let list_cmd = Eiocmd.run ~use_keyeio:false ~info:(Cmd.info "list" ~doc:"List categories for a post") ~app_name:"river" ~service:"river" tag_list in 828 + Cmd.group info [add_cmd; remove_cmd; list_cmd] 829 + 609 830 let main_cmd = 610 831 let doc = "River feed management CLI" in 611 832 let main_info = Cmd.info "river-cli" ~version:"1.0" ~doc in ··· 649 870 ~service:"river" 650 871 html 651 872 in 652 - Cmd.group main_info [user_cmd; sync_cmd; list_cmd; info_cmd; merge_cmd; html_cmd] 873 + Cmd.group main_info [user_cmd; sync_cmd; list_cmd; info_cmd; merge_cmd; html_cmd; category_cmd; tag_cmd]
+58
stack/river/cmd/river_cmd.mli
··· 73 73 Reads: format (atom|jsonfeed), title, limit from command-line arguments. 74 74 Calls: [River.State.export_merged_feed] *) 75 75 76 + (** {2 Category Management Commands} *) 77 + 78 + val category_list : 79 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 80 + (** [category_list] command term for listing all categories. 81 + 82 + Calls: [River.State.list_categories] *) 83 + 84 + val category_add : 85 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 86 + (** [category_add] command term for adding a category. 87 + 88 + Reads: id, name, optional description from command-line arguments. 89 + Calls: [River.State.add_category] *) 90 + 91 + val category_remove : 92 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 93 + (** [category_remove] command term for removing a category. 94 + 95 + Reads: id from command-line arguments. 96 + Calls: [River.State.remove_category] *) 97 + 98 + val category_show : 99 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 100 + (** [category_show] command term for showing category details. 101 + 102 + Reads: id from command-line arguments. 103 + Calls: [River.State.get_category], [River.State.get_posts_by_category] *) 104 + 105 + val category_cmd : int Cmd.t 106 + (** [category_cmd] is the category management command group. *) 107 + 108 + (** {2 Post Tagging Commands} *) 109 + 110 + val tag_add : 111 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 112 + (** [tag_add] command term for tagging a post with a category. 113 + 114 + Reads: post_id, category_id from command-line arguments. 115 + Calls: [River.State.add_post_category] *) 116 + 117 + val tag_remove : 118 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 119 + (** [tag_remove] command term for removing a category from a post. 120 + 121 + Reads: post_id, category_id from command-line arguments. 122 + Calls: [River.State.remove_post_category] *) 123 + 124 + val tag_list : 125 + (Eio_unix.Stdenv.base -> Xdge.t -> Keyeio.Profile.t -> int) Term.t 126 + (** [tag_list] command term for listing categories assigned to a post. 127 + 128 + Reads: post_id from command-line arguments. 129 + Calls: [River.State.get_post_categories] *) 130 + 131 + val tag_cmd : int Cmd.t 132 + (** [tag_cmd] is the post tagging command group. *) 133 + 76 134 (** {2 Main Command} *) 77 135 78 136 val main_cmd : int Cmd.t